Package: django-urlconf-export
Django URLconf ExportDo you need to make URLs for your Django website in another microservice?This used to be painful; you had to hard-code URL logic in multiple places.This was messy and fragile, especially when URLs are translated to multiple languages.But now, Django URLconf Export has solved this problem.It exports your website URLconf in a JSON format, then imports it to any other Python service.So you can make URLs for your website from anywhere, with no hassle, no repetition and no debt.Some example uses:Email microservice that sends links to users.Sitmaps generation microservice.Microservice that buys paid ads for some website pages.Video: 7 minute overviewTable of contentsDjango URLconf ExportVideo: 7 minute overviewUser GuideInstallationExport URLconf as JSONSave URLconf to a fileExample use-caseServe URLconf from an endpointExample use-caseIntegrationExporting from a Django serviceImporting in a non-Django serviceEdge casesImporting in a Django service with own URLsImporting in a Django service with no URLsFeature DetailsExport whitelist and blacklistIncluded URLsI18n URLsExport non-default root URLconfQuality assurance for i18n URLsCheck for translation errors in URL patternsEnsure URL patterns use kwargs, not argsDevelopment GuideRunning testsDevelopingChanging test dependenciesFormatting imports and codePublishing to PyPiFurther DevelopmentUser GuideInstallationThe package is calleddjango-urlconf-exportSome ways to install:pipenvinstalldjango-urlconf-export

pipinstalldjango-urlconf-export

poetryadddjango-urlconf-exportExport URLconf as JSONIf you have this URLconf:urlpatterns=[url(r"^login/$",View.as_view(),name="login"),]You can run this code:fromdjango_urlconf_exportimportexport_urlconfexport_urlconf.as_json()You will get this JSON:[{"regex":"^login/$","name":"login"},]Then somewhere else, you can import the JSON like this:fromdjango_urlconf_exportimportimport_urlconfimport_urlconf.from_json(json_urlpatterns)Then you can callreverseto make urls, just like normal:reverse("login")=="/login/"Save URLconf to a fileIf you adddjango_urlconf_exportto your website'sINSTALLED_APPSyou can run:django-adminexport_urlconf_to_file>"urlconf.json"To create a file calledurlconf.jsonThen you can import the file somewhere else like this:import_urlconf.from_file("urlconf.json")Example use-caseAt Lyst, we have a skeleton repo that we share with digital agencies who create special pages for us likeThe Year in Fashion. The repo is a stripped-down simulation of our production environment. Agencies develop pages for our website within the repo, so integration is easy.We include a URLconf file in the skeleton repo. Before we did this, agencies used to hard-code URLs into their work. But now:They can make URLs in the standard Django way.The URLs are always correct; no silent errors.The URLs are localised for all the languages we support.Serve URLconf from an endpointThis view returns URLconf JSON:fromdjango_urlconf_export.views.exportimportURLConfExportViewurlpatterns=[url(r"^urlconf/",URLConfExportView.as_view()),]Then you can import from a URI like this:import_urlconf.from_uri("/urlconf/")Example use-caseA Lyst we have 3 services that make Lyst website urls:An email service.A sitemaps generation service.A paid advertising purchasing service.These services fetch URLconf from the Lyst website when they boot up, and update it periodically.So when the URLs change, we don't need to update any service code. This is particularly helpful when we add a new language for our localised URLs.IntegrationExporting from a Django serviceIn most situations, the best approach is toserve URLconf from an endpoint.In some situations, it might work better if yousave URLconf to a file.If you have a specialised use-case that isn't handled by either of these approaches, You could roll your own core logic toexport URLconf as JSON.If you roll a bespoke integration you think might be useful to others, please feel free to submit a PR.Importing in a non-Django serviceYou can import URLconf and make URLs in any Python code.First, add Django as a dependency e.g.pip install djangoThen callimport_urlconf.init_django()before you import any URLconf e.g.fromdjango_urlconf_exportimportimport_urlconfimport_urlconf.init_django()import_urlconf.from_uri("https://www.example.com/urlconf/")Then you can callreverse()and make URLs for your website, just like in the website code.Edge casesBy default, Django will be initialized withsettings.ROOT_URLCONF == "imported_urlconf"The module will be created when you import some urlconf.If you need to setsettings.ROOT_URLCONFto different module name, you can:import_urlconf.init_django(ROOT_URLCONF="another_urlconf_module")You can set any other Django settings this way too.Seethe source codefor the default Django settings.Importing in a Django service with own URLsBy default, the library imports URLconf into the root URLconf module of the service -settings.ROOT_URLCONF.But if the service has its own URLs,settings.ROOT_URLCONFwill have some URLconf in it already.To avoid overwriting the service's URLs, you can import to a different module with this Django setting:URLCONF_IMPORT_ROOT_URLCONF="imported_urlconf"Or you can add aurlconf="..."argument when you import:import_urlconf.from_file("urlconf.json",urlconf="imported_urlconf")If the module does not exist, it will be created - so you can call it anything you like.If the module exists, any existingurlpatternswill be overwritten.Then you can make a url like:reverse("login",urlconf="imported_urlconf")Or for convenience, you could make awebsite_urls.pymodule like this:fromdjangoimporturlsasdjango_urlsfromdjango.appsimportAppConfigfromdjango_urlconf_exportimportimport_urlconfclassWebsiteURLsAppConfig(AppConfig):name="website_urls"verbose_name="Make URLs for our website in any Django service."defready(self):"""When Django initializes, get the latest urlconf from our website."""update_urlconf()defupdate_urlconf():"""Download the latest urlconf from our website"""import_urlconf.from_uri("https://www.example.com/urlconf/",urlconf="imported_urlconf")defreverse(*args,**kwargs):"""Thin wrapper for Django's reverse method, to make a URL for our website."""returndjango_urls.reverse(*args,urlconf="imported_urlconf",**kwargs)Adding"website_urls.WebsiteURLsAppConfig"toINSTALLED_APPSin Django setting will import the URLconf when Django starts up.Then you can make URLs for your website by callingwebsite_urls.reverse(...)If you want to update the URLconf later, you can callwebsite_urls.update_urlconf().Importing in a Django service with no URLsIf your Django service doesn't have any URLs of it's own, you can store imported URLconf in the default URLconf module -settings.ROOT_URLCONF.This makes things a bit simpler. You could make awebsite_urls.pymodule like this:fromdjango.appsimportAppConfigfromdjango_urlconf_exportimportimport_urlconfclassWebsiteURLsAppConfig(AppConfig):name="website_urls"verbose_name="Make URLs for our website in any Django service."defready(self):"""When Django initializes, get the latest urlconf from our website."""update_urlconf()defupdate_urlconf():"""Download the latest urlconf from our website"""import_urlconf.from_uri("https://www.example.com/urlconf/")Adding"website_urls.WebsiteURLsAppConfig"toINSTALLED_APPSin Django setting will import the URLconf when Django starts up.Then you can callreverse()and make URLs for your website, just like in the website code:fromdjango.urlsimportreversereverse(...)If you want to update the URLconf later, you can callwebsite_urls.update_urlconf().Feature DetailsIf you prefer to read code than docs, the tests have examples of all feature details:export_urlconf testsimport_urlconf testsExport whitelist and blacklistBy default, all URLs will be exported. But you can set a whitelist and/or blacklist with these Django settings:URLCONF_EXPORT_WHITELIST={"only-show-this-url"}URLCONF_EXPORT_BLACKLIST={"hide-this-url","hide-this-one-too"}The whitelist is applied first, then the blacklist.List items can be regexes, for example"secret-."matches all URL names that start withsecret-likesecret-page-1,secret-page-2etc.The whitelist and blacklist sets are a mixture of:URL namesURL namespacesFor included URLs with anamespace(seeDjango docs) like the Django admin urls, thenamespaceand theurl_namemust bebothbe allowed by the lists.So you can ban all URLs in theadminnamespace withblacklist = {"admin"}.If you want to exportadmin:some-urlbut no otheradminURLs, setwhitelist = {"admin", "some-url"}.Note: if you setwhitelist = {"admin"}no admin URLs will be exported.See theunit testsfor more examples.You can check the whitelist and/or blacklist are working as expected like this:print(export_urlconf.get_all_allowed_url_names())You can also set whitelist or blacklist explicitly when exporting as JSON:export_urlconf.as_json(whitelist={"only-show-this-url"},blacklist={"hide-this-url","hide-this-one-too"})Or when generating a file:django-adminexport_urlconf_to_file\--whitelist'only-show-this-url'\--blacklist'hide-this-url", "hide-this-one-too'\>urlconf.jsonOr when serving from an endpoint:urlpatterns=[url(r"^urlconf/",URLConfExportView.as_view(whitelist={"only-show-this-url"},blacklist={"hide-this-url","hide-this-one-too"})),]Included URLsWe fully support included URLconf. The JSON looks like:{"regex":"^colors/","namespace":None,"app_name":None,"includes":[{"regex":"^red/$","name":"red"},{"regex":"^blue/$","name":"blue"}],}I18n URLsWe fully support internationalized URLs.The JSON looks like:{"regex":{"en-us":"^color/$","en-gb":"^colour/$","fr-fr":"^couleur/$"},"name":"color"}Some websites (e.g. Lyst) only localise URLs at the language-family level.For example,enrather thanen-usanden-gb.If you set this Django setting:URLCONF_EXPORT_LANGUAGE_WITHOUT_COUNTRY=TrueThen you get JSON like:{"regex":{"en":"^color/$","fr":"^couleur/$"},"name":"color"}You can also add an argument when exporting as JSON:export_urlconf.as_json(language_without_country=True)Or when generating a file:django-adminexport_urlconf_to_file--language-without-country>urlconf.jsonOr when serving from an endpoint:urlpatterns=[url(r"^urlconf/",URLConfExportView.as_view(language_without_country=True)),]We support theLocalePrefixPattern(seeDjango docs.So if you have URLconf like:fromdjango.conf.urls.i18nimporti18n_patternsurlpatterns=i18n_patterns(url(r"^$",View.as_view(),name="index"),)You get JSON like:{"isLocalePrefix":True,"classPath":"django.urls.resolvers.LocalePrefixPattern","includes":[{"regex":"^$","name":"index"}],}Note thatclassPathis saved in the JSON. So if (like Lyst) your project uses a subclass of Django'sLocalePrefixPatternit will work.Export non-default root URLconfBy default, we export the root URLconf module that creates the endpoints of your Django website:settings.ROOT_URLCONF. This is almost always what you want.If you need to export from a different root URLconf module, you can use this Django setting:URLCONF_EXPORT_ROOT_URLCONF="path.to.non_default_root_urlconf"Or when exporting as JSON:export_urlconf.as_json("path.to.non_default_root_urlconf")Or when generating a file:django-adminexport_urlconf_to_file\--urlconf'path.to.non_default_root_urlconf'\>urlconf.jsonOr when serving from an endpoint:urlpatterns=[url(r"^urlconf/",URLConfExportView.as_view(urlconf="path.to.non_default_root_urlconf",)),]Quality assurance for i18n URLsThis library is particularly useful if you have internationalized URLs.We provide some methods to help ensure URLs are translated correctly.Check for translation errors in URL patternsIf you want to check that URL pattern kwargs are the same for all translations of a URL, you can add a unit test to your project like:fromdjango_urlconf_exportimporturlconf_qadeftest_for_url_translation_errors():urlconf_qa.assert_url_kwargs_are_the_same_for_all_languages()Ensure URL patterns use kwargs, not argsDjango allows you to make URL patterns that have positional arguments (args) and/or named keyword arguments (kwargs).This flexibility can lead to confusion, particularly in large teams. So it can be helpful to ensure developers only usekwargsand notargs.It's also less error-prone to translate URLs that usekwargs, because translators are free to change the order ofkwargsin the URL to match the word order in their language.For example, at Lyst we have URLs like:Example URLLocalised URL PatternEnglish/gucci-bags/(?P<designer_name>.+)-(?P<product_type>.+)French/sacs-gucci/(?P<product_type>.+)-(?P<designer_name>.+)To enforce that URL patterns always usekwargsand notargs, add a test like this:fromdjango_urlconf_exportimporturlconf_qadeftest_all_urls_use_kwargs():urlconf_qa.assert_all_urls_use_kwargs_not_args()Development GuideRunning testspip install tox(orpip3 install tox)Then runtoxDevelopingpip install --user pipenv(orpip3 install --user pipenv)Then run:pipenv installpipenv shellexitpipenv --venvThe location of the virtual environment will be displayed.Here is aguide for using this venv in PyCharm.Changing test dependenciesYou need topipenv install {new-dependency}and also add the dependency intox.ini.Formatting imports and codeFirst runpipenv shellThen run:isort- format importsblack src/ tests/- format codeThenexitto quit the shell.Publishing to PyPiCreate a new release, and the package will be published automatically by a GitHub action.Further DevelopmentIt would be cool if we could make URLs in JavaScript using the JSON generated by this library. Then we could make URLs on the front-end, and in Node services.Lyst are not working on this at the moment. If this feature would be useful to you, a PR would be very welcome :)