Search code examples
djangodjango-tests

Django: Multiple Databases, Routers and Test Framework


I'm currently writing the tests for our django application. Sadly we had to use a multiple database layout and can't change this. (Distributed databases with multiple backends on different servers across multiple datacenters)

We have two databases:

  1. default databases with django default tables
  2. application databases with some models

For these models we wrote different routers like documented on the django site. Now the problem, if I run python manage.py test customerreceipts the test framework dies after some seconds with the following error:

django.db.utils.ProgrammingError: relation "auth_user" does not exist

I checked the created database and there were no tables. Because of this a query from a model throws the error.

The problem model is (in database 2):

class CustomerReceipts(models.Model):

    def _choices_user():
        users = User.objects.all()
        users = users.order_by('id')
        return [(e.id, e.username) for e in users]

    # General
    receipt_name = models.CharField(max_length=20, verbose_name="Receipt name")  #: Receipt name
    ....

    # Auditing
    owner = models.IntegerField(verbose_name="Owner", choices=[('', '')] + _choices_user())

Because multiple database setup does not support direct links, we use an IntegerField for the owner and the business logic handles the integrity.

The problem is the _choices_user() which sets up an query for the missing table. What I don't understand is why django does not create the table auth_user in the first run. If I remove the app with the causing model, the test framework is working without any problem.

Any ideas how this can be fixed?

Thanks!

Edit: I created a one database setup and tried the same thing. Sadly it throws the same error! I'm confused now. Can someone test this too? Create a model with _choices_user method and run test.


Solution

  • This is not exactly a perfect answer but the only way currently:

    Model (removed choices):

    class CustomerReceipts(models.Model):
        # General
        receipt_name = models.CharField(max_length=20, verbose_name="Receipt name")  #: Receipt name
        ....
    
        # Auditing
        owner = models.IntegerField(verbose_name="Owner")
    

    Admin:

    class CustomerReceiptsAdminForm(forms.ModelForm):
        class Meta:
            model = CustomerReceipts
    
        users = forms.ChoiceField()
    
        def __init__(self, *args, **kwargs):
            super(CustomerReceiptsAdminForm, self).__init__(*args, **kwargs)
            owner = self.instance.owner
            usersAll = User.objects.all()
            usersAll = usersAll.order_by('id')
            available_choices = [(e.id, e.username) for e in usersAll]
            self.fields['users'].choices = available_choices
            self.fields['users'].initial = owner
    
    class CustomerReceipts(admin.ModelAdmin):
        fields = ('abc', 'users')
        list_display = ('abc', 'get_user')
    
        form = CustomerReceiptsAdminForm
    
        def save_model(self, request, obj, form, change):
            obj.owner = form.cleaned_data['users']
            obj.save()
    
        def get_user(self, obj):
            return User.objects.get(id=obj.owner).username
        get_user.short_description = 'User'
    

    This will handle all displaying in the admin view and selects the right customer upon editing.