Django personalized url - python

I need to use sub-domains (i think) on Django. I have many "room" on my project and i need to build url with name of room.
Example : If i have a room named "microsoft" and my domain is http://toto.fr i want to have a final url like : "http://microsoft.toto.fr" to go in room of microsoft. I can have few room then few differents url.
How its possible to have this ? Django Sub-Domains can do this ?
Thanks you !

About subdomains
I don't think there is a need in your case for subdomains. You can very well handle the separation of rooms this way:
http://toto.fr/microsoft/
http://toto.fr/room2/
http://toto.fr/room3/
The difficulty will increase (slightly) if you need to generate these urls (e.g users create new rooms).
Using subdomains
Reroute from Apache, Nginx etc.
This is the way I would go and recommend you.
Each service has its way of doing it so you would need to post a specific question related to the service you will use in production.
--- edit ---
The idea here is to have your urls.py work the way Django is meant to:
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('microsoft/', views.microsoft),
]
So this means on your localhost you will access urls that look like this:
https://localhost:8000/microsoft/something
This is how it is going to work behind the scenes. But you can setup your http server (Apache for example) to allow your users to use:
https://microsoft.example.com/something
Here's an example of configuration (disclaimer I am very much not an expert in this, this might not be exactly the correct syntax):
<VirtualHost *:80>
ServerName microsoft.example.com
RedirectPermanent / http://example.com/microsoft
</VirtualHost>
So when your user types https://microsoft.example.com/something, Apache knows it has to call https://localhost:8000/microsoft/something internally.
--- end edit ---
Use a django extension
django-subdomains does not seem to be active.
--- edit ---
Before going with third party code like django-subdomains you should always check if it is being maintained. You can see on their pypi page that it is not compatible with Django 2 and 3 (and therefore not compatible with python 3). So this should tell you to not go with it.
It would mean for you to start with components that are not being actively maintained and have potential security holes.
--- end edit ---
I tried very quickly to set it up with Django 3 and could get it to work.
There is the this fork that claims compatibility with Django 3 but seems pretty inactive also.

You can handle as many rooms as you want with a variable:
urlpatterns = [
...
path('foo/<str:theroom>/bar/', view_room, name="view-room"),
...
]
If for instance you access
http://youraddress/foo/ubuntu/bar/
then Django will call your view function
def view_room(request, theroom):
with theroom = ubuntu.
If you prefer defining subdomains like http://ubuntu.youraddress, you will have to define one subdomain per room in the httpd config file, but also, to declare these subdomains in an authoritative DNS. That might be complicated, and is not a good idea IMHO, since subdomains are rather used to specify which app is called.

Related

how to add log traces for django's (1.11) URL dispatcher (not for production)

I have an old (Django 1.11) over complicated Django application with overlapping and funnily ordered url matching rules and I want to update it to newer Django versions and to simplify / refactor.
For better understanding Id' like to navigate through the application (manually) and retrieve some traces
Would it be possible to achieve following without having to add log statements in every view.
I'd like to get traces telling me which rule was picked by the url dispatcher (and if possible, but not really necessary), which view with which parameter was called.
I'm for example sure, that some rules can just be deleted as they're no more used.
Example:
If I had following (not complicated with non overlapping rules) url dispacher rules.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app1/', app1.views.top_view),
url(r'^app1/a', app1.views.a_view),
]
and I acessed http://myserver/app1. I'd like to have a trace telling me,
that http://myserver/app1 triggered rule ^app1/ and (not so important) view app1.views.top_view will be used to handle the request.
midllewares, decorators, monkeypatches, whatever will do as long as I don't have to change all individual rules and all views to get my traces.
Even a considerable performance hit is not an issue this is for debugging
P.S.
I am aware of the django-extensions module and the management command show_urls.
This is already helpful, but for more complicated urls showing up in my app I'd like to have the logging.
You may have to upgrade to 2.2 first, or backport this commit else you can't get information from the router about the exact match it has used to resolve the request URI.
That said, it should be done in middleware during request phase. The ResolverMatch instance is available in the WSGIRequest as request.resolver_match. Using the fix above, it's trivial to add a log to each request.
If you cannot backport or upgrade, there is a way to do it, which basically involves building a cache on start up of all known urlconfs and then matching each one and I've done it at some point, yet don't have it laying around any more. You basically have to do the work of URLResolver.resolve() and then add the route to it, when it creates the ResolverMatch instance. Hopefully this helps you get started.

How to dynamically populate CORS_ORIGIN_WHITELIST from the database with django-cors-headers?

I am writing a django application where I have a model called Website which contains websites of people. I only allow people who have their websites in my database to use my Django REST API. I am using the django-cors-headers package to whitelist the domains of people: https://github.com/adamchainz/django-cors-headers.
CORS_ORIGIN_WHITELIST variable in settings.py allows me to white list domains as shown in https://github.com/adamchainz/django-cors-headers#cors_origin_whitelist
The problem is that I have to query my models to get the website domains, append them to a list and then put that list into CORS_ORIGIN_WHITELIST. But I can't do that in settings.py because models are loaded after the app starts and settings.py is the one that starts the app.
Does anyone know a way around that? Any suggestions will be appreciated. Thanks in advance.
django-cors-headers has a signal that allows you to decide whether or not to allow the request to pass. The docs show exactly your use case.
Note that CORS_ORIGIN_WHITELIST is also checked by the cors middleware (the signal response doesn't replace the white list), so you can have both: a static whitelist + a dynamic whitelist that depends on the request. You don't need to check the static whitelist in your signal handler.
django-cors-headers also has a setting CORS_ALLOWED_ORIGIN_REGEXES, which comes in handy if your allowed origins can be written as a regex / regular expression.
For example, you could use this to allow wildcard subdomains:
CORS_ALLOWED_ORIGIN_REGEXES = [
r"^https://\w+\.example\.com$",
]

Setting an alias to a Django "plug-in" app

is there some pythonic way to load a django app trough an alias name? This I think would be one way to make an app more "pluggable-friendly".
there is a pattern used in settings.py:
INSTALLED_APPS = ('... ','...', )
where INSTALLED_APPS is a tuple containing names of apps.
That's fine, but I don't want to put in certain apps explicitly this way.
I would like to have an app handle named say external_login_app mapping to drupal_login or mediawiki_login (to be supplied by the end user or a third party), where name of the actual custom module/app is provided by a string variable in settings.py
The pluggable part (e.g. mediawiki_login) must be a full-fledged app with it's own views, models, forms, etc, while external_login_app must be accessible anywhere in the rest of the code.
The goal here is to decouple distributed code from plugins like that.
edit 1: here is what I'm trying:
in settings.py
EXTERNAL_LOGIN = __import__(EXTERNAL_LOGIN_APP)
#setting name must be upper case according to
#django docs
but my custom login app depends on the settings file too, so looks like I have a circular import problem with errors like module external_login does not have attribute views.
This problem seems to be very insidious, as I am unable to use even simple things like render_to_response shortcut in views imported with __import__ statement in settings.py.
edit 2: after trying a while I found that using __import__() in settings.py call won't work because of almost inevitable circular dependencies
The best working method I found so far is to place __import__() calls into other .py files of the app providing the generic "consumer" interface - the one that calls the plugin functions:
in settings.py: as Michał suggests in his answer
EXTERNAL_LOGIN_APP = 'drupal_login'
INSTALLED_APPS = (
'...',
EXTERNAL_LOGIN_APP,
)
e.g. in app/views.py:
from django.conf import settings
EXTERNAL_LOGIN = __import__(settings.EXTERNAL_LOGIN_APP, \
[], [], ['api','forms'])
def login_view(request, external=False):
if external:
form = EXTERNAL_LOGIN.forms.LoginForm(request.POST)
if form.is_valid():
login = form.cleaned_data['login']
password = form.cleand_data['password']
if EXTERNAL_LOGIN.api.check_password(login, password):
#maybe create local account,
#do local authentication
#set session cookies for the
#external site to synchronize logins, etc.
Set
LOGIN_APP_NAME = 'drupal_login' # or 'mediawiki_login', or whatever
early enough in settings.py, then put LOGIN_APP_NAME (without any quotes around it!) in your INSTALLED_APPS instead of the name of the actual app.
If you need more complex functionality involved in determining what app to use, how about putting something like external_login_app() (a function call -- put no quotes around it!) in INSTALLED_APPS and having the external_login_app function return whatever it is that it should return based on a setting in settings.py, or maybe the contents of a config file somewhere or whatever? (EDIT: Tobu's answer shows what such a function might need to return and how it might go about achieving that with __import__.)
Anyway, I'm not sure that much is achieved in this way in terms of decoupling parts of the site -- if the user still needs to modify settings.py, why not have him / her put in the name of the appropriate app in the right place? (EDIT: OK, so now I sort of see what could be gained with the right solution here... I guess the discussion continues as to how best to do what is required.)
EDIT: I posted this prior to the Original Poster amending the question with any edits, and now edit 2 includes the idea of moving the name of the login app to a separate variable in settings.py and including the variable in INSTALLED_APPS. Now that the two edits to the original question are in place, I guess I can sort of see the problem clearer, although that just makes me think that what the situation calls for is an abstract_login app with backends to support the '*_login' modules dealing with drupal, mediawiki etc. login schemes. Well, I can't say I can provide that sort of thing right now... Hope somebody else can. Will edit again should I believe I have a bright idea simplifying the whole thing beyond the OP's edits.
Defining the app name in settings(as Michał and your edit do) works well:
EXTERNAL_LOGIN_APP = 'drupal_login'
INSTALLED_APPS = (
'...',
EXTERNAL_LOGIN_APP,
)
Also in settings, or in a common module, you could then factor in importing the app:
def external_login_app():
return __import__(EXTERNAL_LOGIN_APP, …)
And use it after your imports:
external_login_app = settings.external_login_app()

Django: Admin with multiple sites & languages

I'm supposed to build some Django apps, that allow you to administer multiple sites through one backend. The contrib.sites framework is quite perfect for my purposes. I can run multiple instances of manage.py with different settings for each site; but how should django's admin deal with different settings for different sites, eg. if they have different sets of languages, a different (default) language? So there are some problem s to face if you have to work on objects coming from different sites in one admin...
I think settings.ADMIN_FOR is supposed to be quite helpful for cases like this, but theres hardly any documentation about it and I think it's not really used in the actual Django version (?).
So any ideas/solutions are welcome and much appreciated!
Thanks a lot...
There is an old blog post by James Bennet which might be helpful:
Create a new Site object in your admin for each domain, and put the id of that Site into its settings file as SITE_ID so Django knows which site in the database corresponds to this settings file.
In the settings file for your original site (the one with id 1), add the other sites’ settings files to the ADMIN_FOR setting, to let Django know that this one instance of the admin application will handle all of the sites.
As documented ADMIN_FOR (for which i can not post link) should be a tuple of settings modules much like INSTALED_APPS is a tuple of django app modules.
Note that blog post is from 2006 so it uses a bit outdated API.

How to make some Django settings accessible by staff?

In Django, settings are stored in a file, settings.py. This file is part of the code, and goes into the repository. It is only the developers who deal with this file. The admin deals with the models, the data in the database. This is the data that the non-development staff edits, and the site visitors see rendered in templates.
The thing is, our site, and many others, have lots of settings options that should be edited by non-developer staff. We're talking about stand-alone site-wide constants that really have no place in the database. Putting them in the database will result in numerous pointless queries. Caching could alleviate that, but that seems unnecessarily complex to handle what can be done with a single line in the settings.py file.
I did notice this dbsettings app, but it is old and unmaintained. I also noticed that the django e-commerce app, Satchmo, includes a use-case specific fork of this dbsettings app. We could build something similar into our site, an app that stores some settings as key/value pairs in a single database table, but it just really seems like the wrong approach. Why put something in the DB that doesn't belong there just to make it more easily editable by non-developers?
We have a list of site-wide settings on our Django site that we want to be editable by non-developer administrators. What is the best way of going about this?
Something like dbsettings (as you mentioned) seems like the way to go. From the reasons for existence for that project:
Not all settings belong in
settings.py, as it has some
particular limitations:
Settings are project-wide. This not only requires apps to clutter up
settings.py, but also increases the chances of naming
conflicts.
Settings are constant throughout an instance of Django. They cannot be
changed without restarting the application.
Settings require a programmer in order to be changed. This is true even
if the setting has no functional impact on anything else.
If dbsettings doesn't work for you, then implement your own, or fork it. It doesn't seem like it'd be too arduous.
I'm actually a big fan of dbsettings, and keep meaning to publish my fork that patches it to work with Django 1.1 (not actually a big change). Looks like someone has updated it already.
However, you're probably right that this is overkill for what you need. One thing I've done before is to add a line to the end of settings.py that imports and parses a YAML file. YAML is a simple markup language, which at its most basic is just KEY: VALUE ...
CONSTANT1: MyValue
CONSTANT2: Anothervalue
If you put this somewhere editors can access it, then at the end of settings.py you just do:
import yaml
try:
globals().update(yaml.load(open('/path/to/my/yaml/file.yml')))
except:
pass
You'll need the Python YAML library to parse the YML file.
The downside to this approach is that you'll need to restart Apache to get it to pick up the changes.
Edited to add It wouldn't be particularly difficult to build a front end which could edit this file, and provide a button which runs a script to restart Apache.
If you must avoid server restarts then a logical place for the settings is the database as Dominic and Daniel said, but you'll need to invalidate cached settings object every time it is updated.
Looks like it's possible to re-set values in the cache with Django's low level cache API. All you want should be achievable with these calls:
cache.set('settings', local_settings)
cache.add('settings', local_settings)
local_settings = cache.get('settings')
cache.delete('settings')
How about putting a sitesettings.py (or whatever) somewhere that your admins can access, then in settings.py do
from sitesettings import *
That seems good and simple, but I may have misunderstood or oversimplified your problem :)
models.py
class Setting(models.Model):
"""Global settings for app"""
name = models.CharField(max_length=100, null=False, blank=False)
value = models.CharField(max_length=100, null=False, blank=False)
def __str__(self):
return self.name
admin.py
from YOUR_APP.models import Setting
class SettingAdmin(admin.ModelAdmin):
list_display = (
'name',
'value',
)
admin.site.register(Setting, SettingAdmin)
extras.py
#register.filter
def get_setting(name):
setting = Setting.objects.get(name=name)
return setting.value
template.html
{% if 'setting_name'|get_setting == 'true' %}
Display your Feature
{% endif %}
Django Packages has a page that lists packages that provide such functionality - most query the database and then use caching to minimize hits on the DB.
I found Django Dynamic Preferences to be of particular interest due to the fine-grained control it gives you over the configuration.

Categories