2

I'm trying to write application for my customer in Django 1.6.4

I've huge experience with Django but this case is quite hard for me to solve.

I'm using Substituting a custom user model feature and my Member class looks like (this is only simple snipplet):

class Member(AbstractBaseUser):
    ...
    date_of_birth = models.DateField()
    height = models.FloatField()
    ...
    REQUIRED_FIELDS = ['date_of_birth', 'height']

My Member can define multiple addresses with different countries. Each country has own specific address validations or extra fields, so I defined main abstract address and some subaddresses:

class COUNTRIES(object):
    POLAND = 'pl'
    GERMANY = 'de'
    @classmethod
    def to_choices(cls):
        return ((cls.POLAND, 'Poland'), (cls.GERMANY, 'Germany'))

class BaseAddress(models.Model):
    class Meta:
        abstract = True
    member = models.ForeignKet(settings.AUTH_USER_MODEL, related_name='+')
    country = models.CharField(max_length=2, choices=COUNTRIES.to_choices)

class AddressPL(BaseAddress):
    street = models.CharField(max_length=80)
    city = models.CharField(max_length=20)
    postal_code = models.CharField(max_length=6, validators=[RegexValidator('^\d{2}-\d{3}$'),])
    def save(self, *args, **kwargs):
        self.country = COUNTRIES.POLAND
        super(AddressPL, self).save(*args, **kwargs)

class AddressDE(BaseAddress):
    street = models.CharField(max_length=80)
    city = models.CharField(max_length=20)
    postal_code = models.CharField(max_length=6, validators=[RegexValidator('^\d{5}$'),])
    def save(self, *args, **kwargs):
        self.country = COUNTRIES.GERMANY
        super(AddressPL, self).save(*args, **kwargs)

As You can see the only change is in postal_code field in validators section. The only problem is that in Member I need to add something like this:

class Member(AbstractBaseUser):
    ...
    @property
    def addresses(self):
        from itertools import chain
        addresses_pl = AddressPL.objects.filter(member=self)
        addresses_de = AddressDE.objects.filter(member=self)
        return list(chain(addresses_pl, addresses_de))

based on 431628-how to combine 2 or more querysets

And my question is. Do You have any other idea to design this? I need to provide easy, scalable solution. Maybe any idea for using proxies?

Community
  • 1
  • 1
WBAR
  • 4,924
  • 7
  • 47
  • 81
  • Seems overkill to me to create another class just for a changing postal code. Maybe you would remove the validor in the model, and create a method that can raises ValidationError when the form is submitted. – David Dahan Jun 11 '14 at 14:48

1 Answers1

1

I think concrete inheritance could be a candidate for your problem:

class Member(AbstractBaseUser):
    ...
    date_of_birth = models.DateField()
    height = models.FloatField()
    ...
    REQUIRED_FIELDS = ['date_of_birth', 'height']

class BaseAddress(models.Model):
    # no abstract = True
    member = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='adresses')
    country = models.CharField(max_length=2, choices=COUNTRIES.to_choices)

class AddressPL(BaseAddress):
    # your custom logic here

class AddressDE(BaseAddress):
    # your custom logic here

Then, if you want to add some data :

m = Member(yourargs)
m.save()

adress1 = AdressPL(yourargs)
adress1.member = m
adress1.save()

adress2 = AdressPL(yourargs)
adress2.member = m
adress2.save()

m = Member.objects.get(pk=m.pk)

m.adresses.all() # will output a list of BaseAdresse instances

This way, adding a new kind of address is just a matter of sublassing BaseAdress. No need to alter your user model.

The drawback of concrete inheritance is that it adds a join to your DB queries on inherited models.

Also, if you go this way, have a look at django-polymorphic, it's a Django app that provide useful method to deal with concrete inheritance.

Agate
  • 3,152
  • 1
  • 19
  • 30