Search code examples
pythondjangodjango-contenttypes

Identify Django User table based on content_type and field name links


I've writing an app for managing subscriptions, for example annual club membership. There are different catergories of membership and these categories can have admin specified criteria which could relate to any table in the database. The only requirement is there must be a link back to the Django User table.

I've therefore got this model (defined below) where:

  • name is the Category name for user convenience
  • content_type is a link to the django_content_type table to identify the table the criteria are being set for this category
  • filter_condition is the condition to be user to select relevant users (for example if the content_type table were User, then this could be as simple as {"is_active":"true"}
  • user_link is the field within the table identified by content_type which is a foreign key for the User table

I want to check the user_link does link to the User table when the admin saves and I'm writing def clean (self) for that purpose.

I cannot work out how to convert my content_type and user_link fields into an object I can use to check it is the User table. Help very welcome!

Here's models.py

from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
import datetime

from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _

from django.contrib.contenttypes.models import ContentType
from subscriptions.fields import JSONField

class Category(models.Model):
    name = models.CharField('Category', max_length=30)
    content_type = models.ForeignKey(ContentType)
    filter_condition = JSONField(default="{}", help_text=_(u"Django ORM compatible lookup kwargs which are used to get the list of objects."))
    user_link = models.CharField(_(u"Link to User table"), max_length=64, help_text=_(u"Name of the model field which links to the User table.  'No-link' means this is the User table."), default="No-link")

    def clean (self):
        if self.user_link == "No-link":
            if self.content_type.app_label == "auth" and self.content_type.model == "user":
                pass
            else:
                raise ValidationError(
                    _("Must specify the field that links to the user table.")
                    )
        else:
            t = getattr(self.content_type, self.user_link)
            ct = ContentType.objects.get_for_model(t)
            if not ct.model == "user":
                raise ValidationError(
                    _("Must specify the field that links to the user table.")
                    )    



    def __unicode__(self):
        return self.name

    def _get_filter(self):
        # simplejson likes to put unicode objects as dictionary keys
        # but keyword arguments must be str type
        fc = {}
        for k,v in self.filter_condition.iteritems():
            fc.update({str(k): v})
        return fc

    def object_list(self):
        return self.content_type.model_class()._default_manager.filter(**self._get_filter())

    def object_count(self):
        return self.object_list().count()

    class Meta:
        verbose_name = _("Category")
        verbose_name_plural = _("Categories")
        ordering = ('name',)

Different discriptions can have different criteria.


Solution

  • For validation purposes I've got the solution below, which replaces the main else clause in def clean.

        else:
            s = apps.get_model(self.content_type.app_label, self.content_type.model)
            if not hasattr(s, self.user_link):
                raise ValidationError(
                    _("Must specify the field that links to the user table.")
                    )
    

    I now need to work out how to actually use the information and connect the two into a field so I can link through to the User table