Specify the database for a Django ModelForm instance - django

How can I specify which database (by its alias name) a Django ModelForm should use?
A Django ModelForm knows its corresponding model, and the fields included.
The ModelForm instance clearly knows how to specify a database, internally. It can validate its fields against the database, and can save a new model instance to the database. This implies its operations have knowledge of which database to use.
I can't find how to specify any database other than the default, when creating the ModelForm nor when it interacts with the database::
import csv
from cumquat_app.forms import CumquatImportForm
db_alias = 'foo'
reader = csv.DictReader(input_file)
for row in reader:
fields = make_fields_from_input_row(reader)
# Wanted: ‘form = CumquatInputForm(fields, using=db_alias)’.
form = CumquatImportForm(fields)
# Wanted: ‘if form.is_valid(using=db_alias)’.
if form.is_valid():
# Wanted: ‘form.save(using=db_alias)’.
form.save()
What I need is to specify the database alias as an external user of the ModelForm, when creating the instance or when calling ModelForm.clean or ModelForm.is_valid or ModelForm.save etc.
The same way I can with the ‘using’ hook of QuerySet.using('foo'),
or Model.save(using='foo').
Note that this is not a job for multi-database routing policy configuration. The use case is that I need to specify exactly one database, only known at run time. If the connection fails it should not fall back to any other, so database routes are the wrong hammer for this nail.
I can request the ModelForm.save method to not commit its change (with commit=False) and then use the Model.save directly. That does not address the other behaviour of a ModelForm which accesses the database, so it is not a solution to this question.
A ModelManager.db_manager could do the job, if I use it to create the model instance. But I'm relying on the form to create the instance; I can't create a model instance because I don't have field values to assign yet. That's the job of the form.
If it matters: this is in a management command, where I need to be able
to specify from the command line that a particular database alias is the
context for a command.
What is the equivalent for using='foo' when instantiating a ModelForm for the model, or calling its methods (ModelForm.clean, ModelForm.save, etc.)?

Unless someone can find a way to do it as requested using the ModelForm interface, I can only conclude Django offers no way to do this with the ModelForm API as it stands.

Related

In a Django Rest Framework view, does request.user make a database call or the database call happens before the request reaches the view?

I need to retrieve some information about my users and I am trying to avoid making unnecessary database calls.
The information I need is stored in three models: User, UserProfile and Membership.
Both UserProfile and Membership have a OneToOne relationship with the User model.
I know that I can use select_related() to retrieve related models from the database in a single call. So I could do something like:
User.objects.select_related('userprofile').select_related('membership').get(id=request.user.id)
But that must not be correct, because if I am using some user information to do the query, it means I already retrieved this information in a previous call.
So what would be the best way to get this information minimising database calls? Would it be even possible to get the information from these 3 models in a single call?
DRF performs user related DB query inside authentication class. See source here. So if you need to optimize this query you should implement custom autentication class(see details here), override authenticate_credentials method and use optimized query inside it.

Does Django create useless migrations?

When I change the verbose_name attribute of a Django Model, Django will generate a related migration (running make migrations command).
However, without applying the migration (migrate command), the change seems to be applied all over the Django project. I think this is because verbose_name is something used at Django level rather than at database level.
This makes me wonder: what is the purpose of this migration file?
Django makes abstraction of the backend used. Indeed, you can use a different backend, by altering the settings.py file. In fact you can define a backend yourself.
All changes to the models thus can have impact on the database. You could for example define a backend that uses the verbose_name of a column as the "comment string" you frequently can add to such column at the database side. If you for example define choices, then a backend could, for some databases, try to enforce these choices at database level.
Since, as said before, Django aims to work unaware of the backend used, it thus aims to be conservative, and make migrations for a lot of model changes. For some of these, the backend will decide to simply do nothing. So no query is constructed, and nothing is changed at the database side. You can indeed see such changes as "useless migrations". But keep in mind that if you later would use a different backend, these "useless migrations" might in fact do something.
Since such migrations do not do anything on the database side, these are however usually not time cosuming. You might want to look to "squash" migration files [Django-doc] together, and thus reduce the number of migration files used. The migrations will still be part of the file, but since there are no queries involved, it will usually not do much harm.
Furthermore, you can in fact patch the deconstruct function on a field, such that the help_text, etc. do not appear in the result of a deconstruct call anymore and thus are not visible to the "detector". This Gist script shows a way to patch the makemigration command:
"""
Patch the creation of database migrations in Django
Import this early from `__init__.py``.
- Don't want verbose_name changes in the migrations file.
- Don't want help_text in the migrations file.
"""
from functools import wraps
from django.db.models import Field
def patch_deconstruct(old_func, condition):
"""
Patch the ``Field.deconstruct`` to remove useless information.
This only happens on internal apps, not third party apps.
"""
#wraps(old_func)
def new_deconstruct(self):
name, path, args, kwargs = old_func(self)
# AutoField has no model on creation, but can be skipped
if hasattr(self, 'model') and condition(self):
kwargs.pop('verbose_name', None)
kwargs.pop('help_text', None)
return name, path, args, kwargs
return new_deconstruct
Field.deconstruct = patch_deconstruct(Field.deconstruct, lambda self: self.model.__module__.startswith('apps.'))

How to dynamically swap default database on the model manager in django?

I am creating a project in django and django rest framework. Its an api for an angular app. The database setup consists of multiple databases. one is default database, all the django tables reside in this database; rest of the databases belong to a type of a user, each user is supposed to have a separate database. So, all the user related data goes to its separate database. To implement the selecting database dynamically, user object has an extra field to store the database to write to.
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
"""Custom User model."""
database= models.CharField(max_length=9)
Reason for doing this was performance improvement as each database is separate, ListView and DetailView would work faster than if the data was stored in the one database only.
I know I can choose a database to store by using the using method on the model manager. In the rest api things work fine and data is being stored in their separate databases, but I end up overriding methods that django has defined. Its adding development cost to the project. Foreign keys and ManytoMany keys needs to be resolved with the current database of the user, which is not happening as I have customized the database setup. Also, my code cant be as good as theirs :p , as they have written django over the course of many years.
I have overwritten many querysets already, but django still uses default database many times. If only I could use the request object in the model manager of django models to swap the default database on per request basis, things would be different i think.
My questions are -
Is there a way to access the request object in the model manager? I could do something to the effect of below code.
class CustomManager(models.Manager):
def get_queryset(self, request):
return super(CustomManager, self).using(request.user.database).get_queryset()
Model manager has _db property that could be used to select database. Would overriding it is advised? if yes, how and where in the code?
Is there a better way to implement the separate databases?
Thanks in advance.
Regards
Using a database router is recommended in Django docs, but the problem is it only accesses the model class.
Found a couple of questions related to the problem of switching databases dynamically. This post has a solution that would solve the problem of passing the request.user or any other parameter by using a threading.local instance.
Someone created a reusable plugin even for this - https://github.com/ambitioninc/django-dynamic-db-router
Hope that helps.

django_auth_ldap vs postgres db using django models

I am creating an app where I store the USERS in a Postgres database with the help of the standard User Model, in my Django app i use Django queries to get all needed information, like "first_name", "username" .. etc
I implemented Django_auth_ldap to start storing user identification data in an Openldap server if i want. But now, i'm confused to how to get the data i used to get using django queries. i don't want to change the behavior in my views, i want to continue using Django queries
This looks like it describes some of what you want: https://django-auth-ldap.readthedocs.io/en/latest/users.html
You can perform arbitrary population of your user models by adding listeners to the Django signal: django_auth_ldap.backend.populate_user. This signal is sent after the user object has been constructed (but not necessarily saved) and any configured attribute mapping has been applied (see below). You can use this to propagate information from the LDAP directory to the user object any way you like. If you need the user object to exist in the database at this point, you can save it in your signal handler or override get_or_build_user(). In either case, the user instance will be saved automatically after the signal handlers are run.

Accessing url of ImageField via values() method on django queryset

I have a data model in Django where I save photos uploaded by users. There's an attribute called image_file in the Photo model (of the type ImageField) where - among other things - the image's url is stored. I can successfully access this url with item.image_file.url in the template (and view) for instance.
However, I can't seem to be able to do the following in the view:
Photo.objects.filter(owner=user).order_by('-id').values('id','image_file.url')[:10]
I.e. For a particular user, I'm trying to get the 10 latest photo objects' image urls along with object ids. This gives me FieldError: Cannot resolve keyword 'image_file.url' into field. Shouldn't this have worked?
I understand I can retrieve the entire object and do the filtering in the template, but I felt it's more optimal to solely retrieve the fields I actually need instead of the full object.
p.s. it's a postgresql backend and the underlying Storage class has been over-ridden
The url is a property, not a value in the database (FileField code) which is why you can't get to it from values(). As far as I can see, you'd have to get the url value separately...
You might want to take a look at only() though. If you go that route, you should probably watch the SQL queries with something like Django Debug Toolbar. If the url property tries to retrieve a field that wasn't included in only(), it will likely make a separate SQL call.