Django Rest Framework: set database dynamically fr

2019-08-11 18:54发布

问题:

I'm trying to find the right way to do this:

Users service:

/api/<country>/users
/api/us/users

That service should use the database corresponding to the country in the URL.

settings.py:

DATABASES = {
'default': {},
'us': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': 'XXX_US', 
    'USER': 'US',
    'PASSWORD': 'XXX',
    'HOST': 'localhost',
    'PORT': '5432',
},
'es': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': 'XXX_ES', 
    'USER': 'ES',
    'PASSWORD': 'XXX',
    'HOST': 'localhost',
    'PORT': '5432',
}  }

To set the database in the ModelViewSet I to this:

class UserViewSet(viewsets.ModelViewSet):

    model = User
    serializer_class = UserSerializer

    def get_queryset(self):
        country = self.kwargs['country']
        return User.objects.using(country).all()

The problem appears when I try to do a POST or PUT. Do I have to overwrite the create() or save() method of the serializer? Is there any other way to do this?

Thank you very much!

回答1:

I think the best place for such functionality is a QuerySet or a ModelManager. For example, the DRF's default serializer uses the default model's manager for creating objects. Unfortunately, the QuerySet doesn't have a way to easily change the current database (self.db) depending on the models' fields, so you'll have to override all the relevant methods.

class UserQuerySet(models.QuerySet):
    def create(self, **kwargs):
        obj = self.model(**kwargs)
        self._for_write = True
        obj.save(force_insert=True, using=kwargs.get('country'))
        return obj

class User(models.Model):
    objects = UserQuerySet.as_manager()


回答2:

Thank you Alex! You answer help me a lot. To fully solve the problem I have to do this:

views.py

class UserViewSet(viewsets.ModelViewSet):

    model = User
    serializer_class = UserSerializer

    def perform_create(self, serializer):        
        serializer.validated_data['country'] = self.kwargs.get('country')
        serializer.create(serializer.validated_data)

And then in the models.py:

class UserQuerySet(models.QuerySet):

    def create(self, **kwargs):
        country = kwargs.get('country')  #Get the country
        kwargs.pop('country')            #Pop the country from kwargs
        obj = self.model(**kwargs)
        self._for_write = True                
        obj.save(force_insert=True, using=country)
        return obj

class User(models.Model):

    objects = UserQuerySet.as_manager()
    id = models.AutoField(primary_key=True,db_column='user_id')
    name = models.CharField(max_length=40)

    class Meta:
        managed = True
        db_table = Constants().USER

And the problem is solved! Thank you a lot.