Tuesday, September 5, 2017

Dynamic model, reverse foreign key not working

Hello,

My problem is very rare so I'll try ad add as much useful detail as possible.

I am using python 3.5 and 3.6, Django 1.11.4

I am creating complex security related application and need to generate additional models based on other applications' models. In general, everything works fine, models are created, migrated without a problem. But there is single problem I totally failed to solve.

Let's say there is preexisting model Alpha of some other application and I dynamically create model Beta which references Alpha with ForeignKey. The problem is that Beta does not get reverse relation, so I can query for beta.alpha, but not for alpha.betas. I receive "django.core.exceptions.FieldError: Cannot resolve keyword 'betas' into field. Choices are: x, y, z"

Here is my code

    def _create_dynamic_model(self, model_label, fields, attributes=None, options=None):
        from django.db import models

        class Meta:
            pass

        setattr(Meta, 'app_label', '_talos')

        if options is not None:
            for key, value in options.items():
                setattr(Meta, key, value)

        attrs = {'__module__': '_talos', '_talos_dynamic': True, 'Meta': Meta}

        if attributes:
            attrs.update(attributes)

        if fields:
            attrs.update(fields)

        model = type(model_label, (models.Model,), attrs)

        return model


I call it like this (where self.model is referenced model class)

    def _create_object_permission_model(self):
        from django.db import models

        return self._create_dynamic_model(
            'o_{0}_{1}'.format(self.model._meta.app_config.label, self.model.__name__),
            fields={
                'role': models.ForeignKey(
                    'talos.Role',
                    related_name='+',
                    on_delete=models.CASCADE),
                'permission': models.ForeignKey(
                    'talos.ObjectPermission',
                    related_name='+',
                    on_delete=models.CASCADE),
                'target': models.ForeignKey(
                    self.model,
                    related_name='talos_permissions', # self.model does not receive reverse relation!
                    on_delete=models.CASCADE)
            },
            options={
                'unique_together': [('role', 'permission', 'target')],
                'index_together': [
                    ('target', 'permission', 'role'),
                    ('target', 'role', 'permission'),
                    ('role', 'target', 'permission')]
            })



I tried calling contribute_to_class and contribute_to_related_class methods manually, tried to call this code at different moments (My application ready method, class_prepared signal handler), with absolutely no luck.

I tried to read Django sources, and got that contribute_to_related_class is called from contribute_to_class with delay, but it did not help me realize what exactly I am doing wrong.

I am trying to use reverse relation from custom view and admin, so everything should be pretty much initialized already.

Roman

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/762bd583-1daa-4229-8355-21c9fc821986%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment