Skip to main content

ModelChoiceField implementation that can accept lists of objects, not just querysets

Project description

The application provides ModelForm, ModelChoiceField, ModelMultipleChoiceField implementations that can accept lists of objects, not just querysets. This can prevent these fields from hitting DB every time they are created.

The problem

Imagine the following form:

class MyForm(forms.Form):
    obj = ModelChoiceField(queryset=MyModel.objects.all())

Every time you render the form ModelChoiceField field will hit DB. What if you don’t want it? Can’t you just pass the list of objects (from cache) to the field? You can’t. What to do? Use CachedModelChoiceField.

The solution

Form with CachedModelChoiceField:

from cached_modelforms import CachedModelChoiceField

class MyForm(forms.Form):
   obj = CachedModelChoiceField(objects=lambda:[obj1, obj2, obj3])

This field will act like regular ModelChoiceField, but you pass a callable that returns the list of objects, not queryset, to it. Calable is needed because we don’t want to evaluate the list out only once.

A callable can return:

  • a list of objects: [obj1, obj2, obj3, ...]. obj should have pk property and be coercible to unicode.

  • a list of tuples: [(pk1, obj1), (pk2, obj2), (pk3, obj3), ...].

  • a dict: {pk1: obj1, pk2: obj2, pk3: obj3, ...}. Note that dict is unsorted so the items will be ordered by pk lexicographically.

Same is for CachedModelMultipleChoiceField.

Warnings

There is no special validation here. The field won’t check that the object is an instance of a particular model, it won’t even check that object is a model instance. And it’s up to you to keep cache relevant. Usually it’s not a problem.

Modelform

But what about modelforms? They still use original ModelChoiceField for ForeignKey fields. This app has its own ModelForm that uses CachedModelChoiceField and CachedModelMultipleChoiceField. The usage is following:

# models.py
class Category(models.Model):
    title = CharField(max_length=64)

class Tag(models.Model):
    title = CharField(max_length=64)

class Product(models.Model):
    title = CharField(max_length=64)
    category = models.ForeignKey(Category)
    tags = models.ManyToManyField(Tag)


# forms.py
class ProductForm(cached_modelforms.ModelForm):
    class Meta:
        model = Product
        objects = {
            'category': lambda:[...], # your callable here
            'tags': lambda:[...], # and here
        }

That’s all. If you don’t specify objects for some field, regular Model[Multiple]ChoiceField will be used.

m2m_initials

If you use ManyToManyField in ModelForm and load an instance to it, it will make one extra DB request (JOINed!) – to get initials for this field. Can we cache it too? Yes. You need a function that accepts model instance and returns a list of pk’s – initials for the field. Here’s a modification of previous example:

# models.py

class Product(models.Model):
    title = CharField(max_length=64)
    category = models.ForeignKey(Category)
    tags = models.ManyToManyField(Tag)

    def tags_cached(self):
        cache_key = 'tags_for_%(product_pk)d' % {'product_pk': self.pk}
        cached = cache.get(cache_key)
        if cached is not None:
            return cached
        result = list(self.tags.all())
        cache.set(cache_key, result)
        return result

# forms.py

class ProductForm(cached_modelforms.ModelForm):
    class Meta:
        model = Product
        objects = {
            'category': lambda:[...], # your callable here
            'tags': lambda:[...], # and here
        }
        m2m_initials = {'tags': lambda instance: [x.pk for x in instance.tags_cached()]}

Compatibility

For sure is works fine with Django 1.2-1.4. Altering ModelForm has required some copy-pasting from Django source code. It couldn’t be done with inheritance. I don’t think there will be problems with futher versions of Django, but don’t forget to run the tests if something seems wrong.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-cached-modelforms-0.2.3.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

django_cached_modelforms-0.2.3-py3-none-any.whl (8.8 kB view details)

Uploaded Python 3

File details

Details for the file django-cached-modelforms-0.2.3.tar.gz.

File metadata

File hashes

Hashes for django-cached-modelforms-0.2.3.tar.gz
Algorithm Hash digest
SHA256 c742227dc4fafd9fd2a75a946dfdfe38a3348504f62791fe96db52bb24e271d3
MD5 92c4791810aca77d34c8d1e202686b69
BLAKE2b-256 88b7f434cff4b7439316f1ec7ddb64d1c44da1a6e7db5e84033faede5c4aafcc

See more details on using hashes here.

File details

Details for the file django_cached_modelforms-0.2.3-py3-none-any.whl.

File metadata

File hashes

Hashes for django_cached_modelforms-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 b46d15e91b592c0fd29638f5420a39242acaffb964d7ed4d6a6d0debaf4fb695
MD5 3ec6fd2c316d21c7118debcd0726d53d
BLAKE2b-256 f7c7a99093dfa8e3d87ae829014059c97554b80461cbcde0b7d10a953601f0af

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page