




This article aims to report the current state of research about dealing with both JS and Django, for people that aim to primarly deal with Django and enjoy the same workflow they have with python package with their frontend development, and find patterns to connect them while keeping them loosely coupled at whatever limit feels right for them. Nuff said, let’s hack !
crudlfap relies on django-webpack-loader, which does its job very well. Also maintaining an override in production project which works in pure webpack.
Read Morehttps://gitlab.com/nerdocs/gdaps
manage.py startproject myproject
. Now install gdaps
as usual: python from gdaps.pluginmanager import PluginManager INSTALLED_APPS = [ # … standard Django apps and GDAPS ‘gdaps’, ] The configuration of GDAPS is bundled in one variable:
python GDAPS = { ‘PLUGIN_PATH’: ‘myproject.plugins’, # default: ‘plugins’ } # Load all plugins from setuptools entry points and from the directory named ‘myproject.plugins’ INSTALLED_APPS += PluginManager.find_plugins() We recommend that you use 'myproject.**plugins**' or just '**plugins**'. Basically, this is all you really need so far, for a minimal working GDAPS-enabled Django application. ### Creating plugins Create plugins using a Django management command: ./manage.py startplugin fooplugin This command asks a few questions, creates a basic Django app in the `PLUGIN_PATH` you chose before, and provides useful defaults as well as a setup.py file. If you use git in your project, install the `gitpython` module (`pip/pipenv install gitpython`). `startplugin` will determine your git user/email automatically and use it for the setup.py file. You now have two choices for this plugin: * add it statically to `INSTALLED_APPS`: see [Static plugins](#static-plugins). * make use of the dynamic loading feature: see [Dynamic plugins](#dynamic-plugins). ### Static plugins In most of the cases, you will ship your application with a few "standard" plugins that are statically installed. These plugins must be loaded *after* the `gdaps` app. Prepend it with the `PLUGIN_PATH` you created before.
python # … INSTALLED_APPS = [ # … standard Django apps and GDAPS ‘gdaps’, # put “static” plugins here too: ‘myproject.plugins.fooplugin’, ] ### Dynamic plugins By installing a plugin with pip/pipenv, you can make your application aware of that plugin too:
bash cd fooplugin pipenv install -e . This installs the plugin as python module into the site-packages and makes it discoverable using setuptools. From this moment on it should be already registered and loaded after a Django server restart. Of course this also works when plugins are installed from PyPi, they don't have to be in the project's `plugins` folder. You can conveniently start developing plugins in there, and later upload them as separate plugins to PyPi. ### Using GDAPS apps #### Interfaces Plugins can define interfaces, which can then be implemented by other plugins. The `startplugin` command will create a `/api/interfaces.py` file automatically. Interfaces don't have to be defined in that module, but it is a recommended coding style for GDAPS plugins:
python from gdaps import Interface class IFooInterface(Interface): “““Documentation of the interface””” def do_something(self): pass You can then easily implement this interface in any other file (in this plugin or in another plugin) using the `implements` decorator syntax:
python from gdaps import implements from myproject.plugins.fooplugin.api.interfaces import IFooInterface @implements(IFooInterface) class OtherPluginClass: def do_something(self): print(‘I did something!’) #### ExtensionPoints An ExtensionPoint (EP) is a plugin hook that refers to an Interface. An EP can be defined anywhere in code. You can then get all the plugins that implement that interface by just iterating over that ExtensionPoint:
python from gdaps import ExtensionPoint from myproject.plugins.fooplugin.api.interfaces import IFooInterface class MyPlugin: ep = ExtensionPoint(IFooInterface) def foo_method(self): for plugin in ep: print plugin().do_domething() Keep in mind that iterating over an ExtensionPoint **does not return instances** of plugins. It just returns the **class** that was decorated with *@implements*. This might be improved in the future (auto-instantiated plugins). #### URLs To let your plugin define some URLs that are automatically detected, you have to add some code to the global urls.py file:
python from gdaps.pluginmanager import PluginManager urlpatterns = [ # … ] # just add this line after the urlpatterns definition: urlpatterns += PluginManager.collect_urls() GDAPS then loads and imports all available plugins' urls.py files, collects their `urlpatterns` variables and merges them into the global one. A typical `fooplugin/urls.py` would look like this: from . import views app_name = fooplugin urlpatterns = [ path('/fooplugin/myurl', views.MyUrlView.as_view()), ] GDAPS lets your plugin create global, root URLs, they are not namespaced. This is because soms plugins need to create URLS for frameworks like DRF, etc. ## Settings GDAPS settings are bundled in a `GDAPS` variable you can add to your settings.py. The defaults are:
python GDAPS = { ‘PLUGIN_PATH’: ‘plugins’ } Explanations of the settings: ##### PLUGIN_PATH This is the (dotted) plugin path used as directory within your main application, and as entry point for setuptools' plugins. The default is 'plugins', so if you name your project "my_project", there will be a `my_project/plugins/` directory where e.g. `./manage.py startplugin` will create its content. ### Custom per-plugin settings GDAPS allows your application to have own settings for each plugin easily, which provide defaults, and can be overridden in the global `settings.py` file. Look at an example conf.py file (created by `./manage.py startplugin fooplugin`):
python from django.test.signals import setting_changed from gdaps.conf import PluginSettings NAMESPACE = ‘FOOPLUGIN’ # Optional defaults. Leave empty if not needed. DEFAULTS = { ‘MY_SETTING’: ‘somevalue’, ‘FOO_PATH’: ‘django.blah.foo’, ‘BAR’: [ ‘baz’, ‘buh’, ], } # Optional list of settings that are allowed to be in ‘string import’ notation. Leave empty if not needed. IMPORT_STRINGS = ( ‘myproject.plugins.fooplugin.models.FooModel’ ) # Optional list of settings that have been removed. Leave empty if not needed. REMOVED_SETTINGS = ( ‘FOO_SETTING’ ) fooplugin_settings = PluginSettings(‘FOOPLUGIN’, None, DEFAULTS, IMPORT_STRINGS) def reload_fooplugin_settings(*args, **kwargs): setting = kwargs[‘setting’] if setting == ‘FOOPLUGIN’: fooplugin_settings.reload() setting_changed.connect(reload_fooplugin_settings) ``` Detailed explanation: ##### DEFAULTS
The DEFAULTS
are, as the name says, a default array of settings. If fooplugin_setting.BLAH is not set by the user, this default value is used. #####
IMPORT_STRINGSSettings in a *dotted* notation are evaluated, they return not the string, but the object they point to. If it does not exist, an
ImportErroris raised. #####
REMOVED_SETTINGSA list of settings that are forbidden to use. If accessed, an
RuntimeErroris raised. This allows very flexible settings - as dependant plugins can easily import the
fooplugin_settingsfrom your
conf.py`. However, the created conf.py file is not needed, so if you don’t use custom settings at all, just delete the file. ## Contributing If you want to contribute, feel free and write a PR, or contact me. ## License I’d like to give back what I received from many Open Source software packages, and keep this library as open as possible, and it should stay so. GDAPS is licensed under the GPL, see LICENSE.md. ## Credits I was majorly influenced by other plugin systems when writing this code, big thanks to them: * The PyUtilib library * The Pretix ecosystem * Yapsy * Django-Rest-FrameworkWell that’s a nice way to automate backups and rollbacks during my deploys and make another CI Dev-Perfect(tm)
https://pypi.org/project/shyml/
Sh and Yml had a baby : ShYml.
In kubernetes, we have the joy of applying configuration directly from maintainer repositories:
kubectl apply -f https://raw.github.../setup.yml
This allows each repository to maintain its own infrastructure configuration as code, and maintaining a core devops framework in between. You will have to deal with particular credentials, that you should use something else to generate and so on.
On the other side of internet, happy docker-composers are also having a lot of fun maintaining them in repositories they contribute to. And there is a new command in town to get a warm automated feeling:
Read MoreThe new version of yourlabs/python docker image, which bundles npm and pip3 on alpine and a bunch of testing tools, was released with bandit and safety baked in.
You can benefit from it in your Open Source software by adding .gitlab-ci.yml:
py-sec-bandit:
image: yourlabs/python
script: bandit -v -x commands,tests {posargs:-r src}
py-sec-safety:
image: yourlabs/python
script: safety check
https://pypi.org/project/mapjoin/
Find str.join boring ? Try mapjoin ^^ Have a great day ;)
https://github.com/TriOptima/tri.form
An alternative to django forms that works with django models and offers a lot of unfrustrating features, tri.form by Anders Hovmöller. Make sure that you also take a look to all their other apps which are pretty interesting !
Still in the competition for most code coverage with least test code that is written manually and needs to be maintained manually, all our test autowriting software has been updated to support a new environment variable : FIXTURE_REWRITE.
Instead of deleting fixtures manually to regenerate them by running the tests, you can now just run your tests with the FIXTURE_REWRITE env var. This will overwrite the fixtures and make the tests look like it passed.
Read More