diff -r 5c925935cbb9 -r d280acf98fb5 ProjectDjango/Project.py --- a/ProjectDjango/Project.py Sat Jan 27 11:46:33 2018 +0100 +++ b/ProjectDjango/Project.py Sat Mar 24 19:11:39 2018 +0100 @@ -97,6 +97,7 @@ Class implementing the Django project support. """ RecentApplicationsKey = "Django/RecentApplications" + RecentDatabaseNamesKey = "Django/RecentDatabaseNames" def __init__(self, plugin, parent=None): """ @@ -120,6 +121,9 @@ self.__recentApplications = [] self.__loadRecentApplications() + self.__recentDatabaseNames = [] + self.__loadRecentDatabaseNames() + self.__recentTestData = { "RecentTestLabels": [], "RecentTestTags": [], @@ -321,6 +325,23 @@ """ Private method to define the database related actions. """ + self.selectDatabaseNameAct = E5Action( + self.tr('Current Database'), + "", + 0, 0, + self, 'django_current_database') + self.selectDatabaseNameAct.setStatusTip(self.tr( + 'Selects the current database')) + self.selectDatabaseNameAct.setWhatsThis(self.tr( + """<b>Current Database</b>""" + """<p>Selects the database name to be used by all database""" + """ actions. An empty database name indicates to use the default""" + """ name.</p>""" + )) + self.selectDatabaseNameAct.triggered.connect(self.__selectDatabaseName) + self.actions.append(self.selectDatabaseNameAct) + self.__setCurrentDatabase(None) + self.inspectDatabaseAct = E5Action( self.tr('Introspect'), self.tr('&Introspect'), @@ -573,6 +594,21 @@ self.runPythonShellAct.triggered.connect(self.__runPythonShell) self.actions.append(self.runPythonShellAct) + self.testEmailAct = E5Action( + self.tr('Send Test Email'), + self.tr('Send Test &Email'), + 0, 0, + self, 'django_tools_sendtestemail') + self.testEmailAct.setStatusTip(self.tr( + 'Send a test email through Django')) + self.testEmailAct.setWhatsThis(self.tr( + """<b>Send Test Email</b>""" + """<p>Sends a test email to confirm email sending through Django""" + """ is working.</p>""" + )) + self.testEmailAct.triggered.connect(self.__sendTestEmail) + self.actions.append(self.testEmailAct) + def __initTestingActions(self): """ Private method to define the testing actions. @@ -863,6 +899,8 @@ menu = QMenu(self.tr("&Database"), self.__ui) menu.setTearOffEnabled(True) + menu.addAction(self.selectDatabaseNameAct) + menu.addSeparator() menu.addAction(self.inspectDatabaseAct) menu.addSeparator() menu.addAction(self.flushDatabaseAct) @@ -943,6 +981,9 @@ menu.addAction(self.diffSettingsAct) menu.addSeparator() menu.addAction(self.runPythonShellAct) + if self.getDjangoVersion() >= (1, 9, 0): + menu.addSeparator() + menu.addAction(self.testEmailAct) self.__menus["tools"] = menu @@ -1360,12 +1401,14 @@ else: if language == "Python2": cmds = ["django-admin2.py", "django-admin2", - "django-admin.py-2.7", "django-admin.py-2.6"] + "django-admin.py-2.7", "django-admin.py-2.6" + ] elif language == "Python3": cmds = ["django-admin3.py", "django-admin3", - "django-admin.py-3.6", "django-admin.py-3.5", - "django-admin.py-3.4", "django-admin.py-3.3", - "django-admin.py-3.2"] + "django-admin.py-3.7", "django-admin.py-3.6", + "django-admin.py-3.5", "django-admin.py-3.4", + "django-admin.py-3.3", "django-admin.py-3.2", + ] else: cmds = [] cmds.extend(["django-admin.py", "django-admin"]) @@ -2028,7 +2071,8 @@ args.append("runserver") if self.__plugin.getPreferences("UseIPv6"): args.append("--ipv6") - # TODO: add support for --nothreading + if not self.__plugin.getPreferences("UseThreading"): + args.append("--nothreading") addr = self.__plugin.getPreferences("ServerAddress") if addr: args.append(addr) @@ -2103,6 +2147,105 @@ self.__ui.launchHelpViewer(url) ################################################################## + ## slots below implement functions to save and load recently used + ## database names + ################################################################## + + def __loadRecentDatabaseNames(self): + """ + Private method to load the list of recently used database names. + """ + self.__recentDatabaseNames = [""] + Preferences.Prefs.rsettings.sync() + rdb = Preferences.Prefs.rsettings.value(self.RecentDatabaseNamesKey) + if rdb is not None: + maxRecentDatabaseNames = \ + self.__plugin.getPreferences("RecentNumberDatabaseNames") + self.__recentDatabaseNames = rdb[:maxRecentDatabaseNames] + + def __saveRecentDatabaseNames(self): + """ + Private method to save the list of recently used database names. + """ + Preferences.Prefs.rsettings.setValue(self.RecentDatabaseNamesKey, + self.__recentDatabaseNames) + Preferences.Prefs.rsettings.sync() + + def getRecentDatabaseNames(self): + """ + Public method to get the list of recently used database names. + + @return list of recently used database names + @rtype list of str + """ + self.__loadRecentDatabaseNames() + return self.__recentDatabaseNames + + def setMostRecentDatabaseNames(self, dbName): + """ + Public method to set the most recently used database names. + + @param dbName database name + @type str + """ + if dbName in self.__recentDatabaseNames: + self.__recentDatabaseNames.remove(dbName) + self.__recentDatabaseNames.insert(0, dbName) + + maxRecentDatabaseNames = \ + self.__plugin.getPreferences("RecentNumberDatabaseNames") + if len(self.__recentDatabaseNames) > maxRecentDatabaseNames: + self.__recentDatabaseNames = \ + self.__recentDatabaseNames[:maxRecentDatabaseNames] + self.__saveRecentDatabaseNames() + + def __selectDatabaseName(self): + """ + Private method to select the name of the database to work with. + """ + recentDatabases = self.getRecentDatabaseNames()[:] + if "" not in recentDatabases: + recentDatabases.insert(1, "") + + selectedDatabase, ok = QInputDialog.getItem( + self.__ui, + self.tr("Database Name"), + self.tr("Select a database name (leave empty for default):"), + recentDatabases, + 0, True) + + if ok: + self.setMostRecentDatabaseNames(selectedDatabase) + self.__setCurrentDatabase(selectedDatabase) + + def __setCurrentDatabase(self, database): + """ + Private method to set the database name to be used. + + @param database name of the database + @type str + """ + if database is None: + database = self.getRecentDatabaseNames()[0] + + self.__currentDatabase = database + if database: + curDb = database + else: + curDb = self.tr("<default>") + self.selectDatabaseNameAct.setText( + self.tr('&Current Database ({0})').format(curDb)) + + def currentDatabase(self): + """ + Public method to get the database name to be used. + + @return database name + @rtype str + """ + return self.__currentDatabase + + ################################################################## ## slots below implement database related functions ################################################################## @@ -2117,7 +2260,8 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("inspectdb") - # TODO: add support for --database DATABASE for Django 1.9.0+ + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) try: path = self.__sitePath() @@ -2152,7 +2296,8 @@ args.append("manage.py") args.append("flush") args.append("--noinput") - # TODO: add support for --database DATABASE for Django 1.9.0+ + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) dia = DjangoDialog( title, @@ -2174,7 +2319,8 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("dbshell") - # TODO: add support for --database DATABASE for Django 1.9.0+ + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) try: wd = self.__sitePath() self.__adjustWorkingDirectory(args, wd) @@ -2216,7 +2362,8 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append(command) - # TODO: add support for --database DATABASE for Django 1.9.0+ + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) args += apps fileFilter = self.tr("SQL Files (*.sql)") @@ -2330,9 +2477,10 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("sqlmigrate") + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) if backwards: args.append("--backwards") - # TODO: add support for --database DATABASE for Django 1.9.0+ args.append(app) args.append(migration) @@ -2361,7 +2509,8 @@ self.__migrationsListDialog = DjangoMigrationsListDialog( DjangoMigrationsListDialog.MigrationsListMode, self, self.__ui) self.__migrationsListDialog.show() - self.__migrationsListDialog.start(self.__getPythonExecutable(), path) + self.__migrationsListDialog.start(self.__getPythonExecutable(), path, + self.__currentDatabase) def __showMigrationsPlan(self): """ @@ -2376,7 +2525,8 @@ self.__migrationsPlanDialog = DjangoMigrationsListDialog( DjangoMigrationsListDialog.MigrationsPlanMode, self, self.__ui) self.__migrationsPlanDialog.show() - self.__migrationsPlanDialog.start(self.__getPythonExecutable(), path) + self.__migrationsPlanDialog.start(self.__getPythonExecutable(), path, + self.__currentDatabase) def __applyAllMigrations(self): """ @@ -2475,7 +2625,9 @@ args.append("manage.py") args.append("showmigrations") args.append("--list") - # TODO: add support for --database DATABASE for Django 1.9.0+ + if self.getDjangoVersion() >= (1, 9, 0): + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) migrations = {} proc = QProcess() @@ -2509,13 +2661,14 @@ from .DjangoMakeMigrationsDialog import DjangoMakeMigrationsDialog dlg = DjangoMakeMigrationsDialog(self.getRecentApplications()) if dlg.exec_() == QDialog.Accepted: - apps, migration, dryRun, empty = dlg.getData() + apps, migration, dryRun, empty, merge = dlg.getData() if apps: self.setMostRecentApplication(apps) apps = apps.split() - self.makeMigrations(apps, migration, dryRun, empty) + self.makeMigrations(apps, migration, dryRun, empty, merge) - def makeMigrations(self, apps, migration=None, dryRun=False, empty=False): + def makeMigrations(self, apps, migration=None, dryRun=False, empty=False, + merge=False): """ Public method to generate migrations. @@ -2527,6 +2680,8 @@ @type bool @param empty flag indicating the creation of an empty migration @type bool + @param merge flag indicating to fix migration conflicts + @type bool """ title = self.tr("Make Migrations") @@ -2546,7 +2701,8 @@ args.append("--dry-run") if empty: args.append("--empty") - # TODO: add support for --merge (Enables fixing of migration conflicts.) + if merge: + args.append("--merge") if apps: args += apps @@ -2570,34 +2726,35 @@ from .DjangoSquashMigrationSelectionDialog import \ DjangoSquashMigrationSelectionDialog dlg = DjangoSquashMigrationSelectionDialog( - migrations, self.getDjangoVersion() >= (1, 9, 0)) + migrations, self) if dlg.exec_() == QDialog.Accepted: - app, start, end, noOptimize = dlg.getData() - - title = self.tr("Squash Migrations") - - try: - path = self.__sitePath() - except DjangoNoSiteSelectedException: - return - - args = [] - args.append(self.__getPythonExecutable()) - args.append("manage.py") - args.append("squashmigrations") - args.append("--noinput") - if noOptimize: - args.append("--no-optimize") - # TODO: add --squashed-name SQUASHED_NAME for Django 2.0.0+ - args.append(app) - if start: - args.append(start) - args.append(end) - - dia = DjangoDialog(title) - res = dia.startProcess(args, path) - if res: - dia.exec_() + app, start, end, noOptimize, name = dlg.getData() + + title = self.tr("Squash Migrations") + + try: + path = self.__sitePath() + except DjangoNoSiteSelectedException: + return + + args = [] + args.append(self.__getPythonExecutable()) + args.append("manage.py") + args.append("squashmigrations") + args.append("--noinput") + if noOptimize: + args.append("--no-optimize") + if name: + args.append("--squashed-name={0}".format(name)) + args.append(app) + if start: + args.append(start) + args.append(end) + + dia = DjangoDialog(title) + res = dia.startProcess(args, path) + if res: + dia.exec_() ################################################################## ## slots below implement some tool functions @@ -2609,23 +2766,31 @@ """ title = self.tr("Diff Settings") - args = [] - args.append(self.__getPythonExecutable()) - args.append("manage.py") - args.append("diffsettings") - # TODO: add support for --default MODULE as of Django 1.11.0 - # TODO: add support for --output {hash,unified} as of Django 2.0.0 - # with hash being the default - - try: - path = self.__sitePath() - except DjangoNoSiteSelectedException: - return - - dia = DjangoDialog(title, fixed=True, linewrap=False) - res = dia.startProcess(args, path, False) - if res: - dia.exec_() + from .DjangoDiffsettingsDataDialog import DjangoDiffsettingsDataDialog + dlg = DjangoDiffsettingsDataDialog(self, self.__ui) + if dlg.exec_() == QDialog.Accepted: + showAll, defaultModule, outputFormat = dlg.getData() + + args = [] + args.append(self.__getPythonExecutable()) + args.append("manage.py") + args.append("diffsettings") + if showAll: + args.append("--all") + if defaultModule: + args.append("--default={0}".format(defaultModule)) + if outputFormat: + args.append("--output={0}".format(outputFormat)) + + try: + path = self.__sitePath() + except DjangoNoSiteSelectedException: + return + + dia = DjangoDialog(title, fixed=True, linewrap=False) + res = dia.startProcess(args, path, False) + if res: + dia.exec_() def __runPythonShell(self): """ @@ -2640,14 +2805,22 @@ args.append("manage.py") args.append("shell") language = self.__e5project.getProjectLanguage() - # TODO: add support for --interface {ipython,bpython,python} as of Djanngo 1.10.0 - # TODO: don't use this for Django 1.10.0+ if language == "Python2": - if self.__plugin.getPreferences("UsePlainPythonPy2"): - args.append("--plain") + if self.getDjangoVersion() < (1, 10, 0): + if (self.__plugin.getPreferences("Python2ConsoleType") == + "python"): + args.append("--plain") + else: + args.append("--interface={0}".format( + self.__plugin.getPreferences("Python2ConsoleType"))) else: - if self.__plugin.getPreferences("UsePlainPythonPy3"): - args.append("--plain") + if self.getDjangoVersion() < (1, 10, 0): + if (self.__plugin.getPreferences("Python3ConsoleType") == + "python"): + args.append("--plain") + else: + args.append("--interface={0}".format( + self.__plugin.getPreferences("Python3ConsoleType"))) try: wd = self.__sitePath() self.__adjustWorkingDirectory(args, wd) @@ -2660,6 +2833,46 @@ except DjangoNoSiteSelectedException: pass + def __sendTestEmail(self): + """ + Private slot to send a test email through Django. + """ + if self.getDjangoVersion() < (1, 9, 0): + return + + title = self.tr("Send Test Email") + + from .DjangoSendTestEmailDataDialog import \ + DjangoSendTestEmailDataDialog + dlg = DjangoSendTestEmailDataDialog(self.__ui) + if dlg.exec_() == QDialog.Accepted: + managers, admins, recipients = dlg.getData() + + args = [] + args.append(self.__getPythonExecutable()) + args.append("manage.py") + args.append("sendtestemail") + if managers: + args.append("--managers") + if admins: + args.append("--admins") + args.extend(recipients) + + try: + path = self.__sitePath() + except DjangoNoSiteSelectedException: + return + + dia = DjangoDialog( + title, + linewrap=False, + msgSuccess=self.tr("Test Email sent successfully."), + msgError=self.tr("Test Email could not be sent.") + ) + res = dia.startProcess(args, path, False) + if res: + dia.exec_() + ################################################################## ## slots below implement caching functions ################################################################## @@ -2668,8 +2881,6 @@ """ Private slot to create the tables for the SQL caching backend. """ - # TODO: this doesn't need the tables anymore - # TODO: add support for the --database DATABASE option for Django 1.9.0+ title = self.tr("Create Cache Tables") try: @@ -2677,27 +2888,19 @@ except DjangoNoSiteSelectedException: return - tblStr, ok = QInputDialog.getText( - self.__ui, + args = [] + args.append(self.__getPythonExecutable()) + args.append("manage.py") + args.append("createcachetable") + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) + + dia = DjangoDialog( title, - self.tr("Enter the names of the cache tables separated by" - " spaces."), - QLineEdit.Normal) - if ok and tblStr != "": - tableNames = tblStr.split() - - args = [] - args.append(self.__getPythonExecutable()) - args.append("manage.py") - args.append("createcachetable") - args += tableNames - - dia = DjangoDialog( - title, - msgSuccess=self.tr("Cache tables created successfully.")) - res = dia.startProcess(args, wd) - if res: - dia.exec_() + msgSuccess=self.tr("Cache tables created successfully.")) + res = dia.startProcess(args, wd) + if res: + dia.exec_() ################################################################## ## slots below implement testing functions @@ -2727,7 +2930,10 @@ args.append("--indent={0}".format(indent)) for excl in excls: args.append("--exclude={0}".format(excl)) - # TODO: add support for --database DATABASE for Django 1.9.0+ + if self.getDjangoVersion() >= (1, 9, 0): + if self.__currentDatabase: + args.append("--database={0}".format( + self.__currentDatabase)) args += appls if dumpFormat == "json": @@ -2754,20 +2960,24 @@ except DjangoNoSiteSelectedException: return - # TODO: use the old DjangoLoaddataDataDialog for __runTestServer before modifying this from .DjangoLoaddataDataDialog import DjangoLoaddataDataDialog dlg = DjangoLoaddataDataDialog(self, self.__ui) if dlg.exec_() == QDialog.Accepted: - fixtures = dlg.getData() + fixtures, excludes, appLabel, ignore = dlg.getData() args = [] args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("loaddata") - # TODO: add support for --exclude EXCLUDE as of Django 1.11.0 - # TODO: add support for --ignorenonexistent - # TODO: add support for --database DATABASE for Django 1.9.0+ - # TODO: add support for --app APP_LABEL + if self.getDjangoVersion() >= (1, 11, 0): + for excl in excludes: + args.append("--exclude={0}".format(excl)) + if ignore: + args.append("--ignorenonexistent") + if appLabel: + args.append("--app={0}".format(appLabel)) + if self.__currentDatabase: + args.append("--database={0}".format(self.__currentDatabase)) args += fixtures dia = DjangoDialog(title) @@ -2835,9 +3045,9 @@ consoleCmd = self.__isSpawningConsole( self.__plugin.getPreferences("ConsoleCommand"))[1] if consoleCmd: - # TODO: use the old DjangoLoaddataDataDialog for this - from .DjangoLoaddataDataDialog import DjangoLoaddataDataDialog - dlg = DjangoLoaddataDataDialog(self, self.__ui) + from .DjangoRunTestServerDataDialog import \ + DjangoRunTestServerDataDialog + dlg = DjangoRunTestServerDataDialog(self, self.__ui) if dlg.exec_() == QDialog.Accepted: fixtures = dlg.getData() @@ -3046,9 +3256,7 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("makemessages") - # TODO: rename this --locale - args.append("-l") - args.append(code) + args.append("--locale={0}".format(code)) try: wd = self.__sitePath() @@ -3100,11 +3308,8 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("makemessages") - # TODO: this seems to be not supported anymore args.append("--no-obsolete") - # TODO: rename this --locale - args.append("-l") - args.append(locale) + args.append("--locale={0}".format(locale)) argsLists.append(args) if len(argsLists) == 0: @@ -3150,9 +3355,7 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("makemessages") - # TODO: rename this --locale - args.append("-l") - args.append(locale) + args.append("--locale={0}".format(locale)) argsLists.append(args) if len(argsLists) == 0: @@ -3181,8 +3384,7 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("makemessages") - args.append("-a") - # TODO: this seems to be not supported anymore + args.append("--all") args.append("--no-obsolete") try: @@ -3215,7 +3417,7 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("makemessages") - args.append("-a") + args.append("--all") try: wd = self.__sitePath() @@ -3262,9 +3464,10 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("compilemessages") - args.append("-l") - args.append(locale) - # TODO: add support for --use-fuzzy per Preferences + args.append("--locale={0}".format(locale)) + if self.getDjangoVersion() >= (1, 8, 0): + if self.__plugin.getPreferences("FuzzyTranslations"): + args.append("--use-fuzzy") argsLists.append(args) if len(argsLists) == 0: @@ -3300,7 +3503,9 @@ args.append(self.__getPythonExecutable()) args.append("manage.py") args.append("compilemessages") - # TODO: add support for --use-fuzzy per Preferences + if self.getDjangoVersion() >= (1, 8, 0): + if self.__plugin.getPreferences("FuzzyTranslations"): + args.append("--use-fuzzy") try: wd = self.__sitePath() @@ -3389,8 +3594,3 @@ res = dia.startProcess(args, path, mergedOutput=True) if res: dia.exec_() - - # TODO: add support for sendtestemail for Django 1.9.0+ - # sendtestemail [email [email ...]] - # --managers Mails the email addresses specified in MANAGERS using mail_managers(). - # --admins Mails the email addresses specified in ADMINS using mail_admins().