How to use different databases depending on login state in Django?

In principle, in order not to worry about replication delays if the user is logged in, we want them to read / write to the master; but if the user is not logged in, we want them to read the replica and write to the wizard. Is this possible using Django routers?

+4
source share
3 answers

This may be bad practice, but at any point in your code, if you use Django> = 1.2, using the using() and using methods, you have full control over which database QuerySets selects and save and delete it.

For example, for QuerySet:

 >>> MyModel.objects.using('replica').all() 

or save an object:

 >>> my_object.save(using='master') 

This way you can always do something like this in a view:

 query_set = MyModel.objects.all() if request.user.is_authenticated(): query_set = query_set.using('master') 

But it can become very cumbersome, and if you decide to go this route, you should look for more elegant ways to mitigate this. Signals , middlewares and / or model managers come to mind.

See Manually select a database from the Django documentation.

+5
source
 class MasterSlaveRouter(object): """A router that sets up a simple master/slave configuration""" def db_for_read(self, model, **hints): "Point all read operations to a random slave" return random.choice(['slave1','slave2']) def db_for_write(self, model, **hints): "Point all write operations to the master" return 'master' def allow_relation(self, obj1, obj2, **hints): "Allow any relation between two objects in the db pool" db_list = ('master','slave1','slave2') if obj1._state.db in db_list and obj2._state.db in db_list: return True return None def allow_syncdb(self, db, model): "Explicitly put all models on all databases." return True 

From django docs

This may be if you really want to do it as an exercise, but I would not recommend it if that happens.

You are requesting that your database replicate for you, which is the right way to do this.

Then in your application you basically say that you want to jump in the middle of this replication and write to a subordinate, and then read from the master; in other words, you are trying to use replication just like a cluster. This can only lead to bad things in the future; concurrency for one will be a problem. A connection unites another, and data integrity - a third.

Another approach to reducing response time - if the user has an account, uploads their information to the back cache of a quick cache, such as redis or couchdb - depending on your preference for key / value storage and document-based storage.

For guest users, since they will not make as many records as they read, this will reduce the load from your db; not to mention performance improvements for registered users.

+1
source

Probably yes. Take a look at the django-multi-db project. It provides a simple MasterSlaveRouter, which is almost identical to that specified in the Django documentation. It also provides a PinningMasterSlaveRouter program that addresses your specific problem. From their documentation:

In some applications, the gap between the master receiving the recording and its replication to the slaves is enough to cause inconsistency for the end user. For example, imagine a scenario with 1 second replication lag. If the user makes a message on the forum (to the master) and then redirects to a fully visualized view (starting with the slave) after 500 ms, the view will fail. If this is a problem in your application, consider using multidb.PinningMasterSlaveRouter. This router works in conjunction with multidb.middleware.PinningRouterMiddleware to ensure that after writing to the default database, future reads from the same user agent are directed to the default database for a custom time period.

PiningMasterSlaveRouter will select a read-only wizard if it thinks that a database record was made during this request. This is probably the best strategy that the wizard always reads to log in, but if you are sure what you want, then you can still achieve it with a little extra middleware:

 class SelectMasterForLoggedInUsers(object): def process_response(self, request, response): if request.user.is_authenticated(): response._db_write = True 
0
source

Source: https://habr.com/ru/post/1386524/


All Articles