Login

Modifying the fields of a third/existing model class

Author:
marinho
Posted:
December 26, 2010
Language:
Python
Version:
1.1
Tags:
fields model helper change
Score:
1 (after 1 ratings)

You can extend the class ModifiedModel to set new fields, replace existing or exclude any fields from a model class without patch or change the original code.

my_app/models.py

from django.db import models

class CustomerType(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

class Customer(models.Model):
    name = models.CharField(max_length=50)
    type = models.ForeignKey('CustomerType')
    is_active = models.BooleanField(default=True, blank=True)
    employer = models.CharField(max_length=100)

    def __unicode__(self):
        return self.name

another_app/models.py

from django.db import models
from django.contrib.auth.models import User

from this_snippet import ModifiedModel

class City(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

class HelperCustomerType(ModifiedModel):
    class Meta:
        model = 'my_app.CustomerType'

    description = models.TextField()

class HelperCustomer(ModifiedModel):
    class Meta:
        model = 'my_app.Customer'
        exclude = ('employer',)

    type = models.CharField(max_length=50) # Replaced
    address = models.CharField(max_length=100)
    city = models.ForeignKey(City)

    def __unicode__(self):
        return '%s - %s'%(self.pk, self.name)

class HelperUser(ModifiedModel):
    class Meta:
        model = User

    website = models.URLField(blank=True, verify_exists=False)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""
Alert: tested only with revision #15056 in development (never tested in production).
"""
import types

from django.db import models
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_model
from django.db.models.fields import FieldDoesNotExist

class ModifiedModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        new_class = super(ModifiedModelMetaclass, cls).__new__(cls, name, bases, attrs)

        if name == 'ModifiedModel' and bases[0] == object:
            return new_class

        try:
            meta = attrs['Meta']()
        except KeyError:
            raise ImproperlyConfigured("Helper class %s hasn't a Meta subclass!" % name)

        # Find model class for this helper
        if isinstance(getattr(meta, 'model', None), basestring):
            model_class = get_model(*meta.model.split('.'))
        elif issubclass(getattr(meta, 'model', None), models.Model):
            model_class = meta.model
        else:
            raise ImproperlyConfigured("Model informed by Meta subclass of %s is improperly!" % name)

        def remove_field(f_name):
            # Removes the field form local fields list
            model_class._meta.local_fields = [f for f in model_class._meta.local_fields
                    if f.name != f_name]

            # Removes the field setter if exists
            if hasattr(model_class, f_name):
                delattr(model_class, f_name)

        # Removes fields setted in attribute 'exclude'
        if isinstance(getattr(meta, 'exclude', None), (list,tuple)):
            for f_name in meta.exclude:
                remove_field(f_name)

        # Calls 'contribute_to_class' from field to sender class
        for f_name, field in attrs.items():
            if isinstance(field, models.Field):
                # Removes the field if it already exists
                remove_field(f_name)

                # Appends the new field to model class
                field.contribute_to_class(model_class, f_name)

        # Attaches methods
        for m_name, func in attrs.items():
            if callable(func) and type(func) == types.FunctionType:
                setattr(model_class, m_name, func)

        new_class._meta = meta

        return new_class

class ModifiedModel(object):
    __metaclass__ = ModifiedModelMetaclass

More like this

  1. Signal to post new saved objects to Twitter by arthurfurlan 6 years ago
  2. Multiple querysets by t_rybik 5 years ago
  3. PreSaveMiddleware by pterk 7 years, 3 months ago
  4. Select multiple using a manytomany checkbox by fivethreeo 7 years, 3 months ago
  5. Finding related objects for instances in a queryset by akaihola 3 years, 11 months ago

Comments

iElectric (on September 10, 2011):

This is pretty nice, does it work with South?

#

Please login first before commenting.