Model design question

19 views
Skip to first unread message

oneroler

unread,
May 17, 2012, 5:02:47 PM5/17/12
to django...@googlegroups.com
I'm trying to setup my first app and I'm trying to figure out the best way to have constraints on a particular field (strategy for class Division noted below).  Below is the basic model structure.  What I would like is for the strategy under a Division to be constrained to the strategies selected for the Business.  A business may have many strategies, but a division will only have one (but it should only be one selected for the business).  Any help on this would be appreciated.  Thanks, Sam

class Strategy(models.Model):
    name = models.CharField(max_length=200)

#name would be something like retail, wholesale, etc

class Business(models.Model):
   name = models.CharField()
   strategy = models.ManyToManyField(Strategy)

class Division(models.Model):
    business = models.ForeignKey(Business) 
    name = models.CharField()
    strategy = ???

Mike Dewhirst

unread,
May 17, 2012, 8:55:11 PM5/17/12
to django...@googlegroups.com
Try ...

strategy = models.ForeignKey(Strategy)


>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/django-users/-/sunwQb8Ft0cJ.
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-users...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/django-users?hl=en.

oneroler

unread,
May 17, 2012, 10:25:34 PM5/17/12
to django...@googlegroups.com
Thanks Mike, that is what I was originally planning to do but realized there would be situations where that wouldn't do exactly what I wanted.  For example, if there is a business that only has the strategy 'wholesale' assigned, using ForeignKey would still allow me to assign a different strategy to a division.  I was hoping to find a solution where the strategy for a division is constrained by the strategies assigned to its respective business.
> django-users+unsubscribe@googlegroups.com.

Mike Dewhirst

unread,
May 18, 2012, 1:02:58 AM5/18/12
to django...@googlegroups.com
On 18/05/2012 12:25pm, oneroler wrote:
> Thanks Mike, that is what I was originally planning to do but realized
> there would be situations where that wouldn't do exactly what I
> wanted. For example, if there is a business that only has the
> strategy 'wholesale' assigned, using ForeignKey would still allow me
> to assign a different strategy to a division. I was hoping to find a
> solution where the strategy for a division is constrained by the
> strategies assigned to its respective business.

It is done in the Admin by nesting admin.StackedInline classes.
Essentially a queryset provides the choices for selecting a Strategy
from those belonging to the Division's parent Business. Perhaps that is
the way to do it in your forms.

To prevent incorrect Strategy assignment, I would build a
Division.clean() method which tests whether the Division.strategy is valid.

I'm a beginner at this but here is a stab at a Division.clean() and
maybe a dguru can contribute ...

Bear in mind that there will be an automatically created
business_strategy table to carry all the many-to-many relationships. I
would replace it with my own called (say) BusinessStrategy and in the
Business model use the 'through' attribute to specify that table.

class BusinessStrategy(models.Model):
business = models.ForeignKey(Business)
strategy = models.ForeignKey(Strategy)

Then I could make a Division.clean() method something like this ...

def clean(self):
for item in BusinessStrategy.objects.all(business=self.business):
if self.strategy == item.strategy:
return True
raise ValidationError(u'Invalid strategy)
> <https://groups.google.com/d/msg/django-users/-/sunwQb8Ft0cJ>.
> > To post to this group, send email to
> django...@googlegroups.com <mailto:django...@googlegroups.com>.
> > To unsubscribe from this group, send email to
> > django-users...@googlegroups.com
> <mailto:django-users%2Bunsu...@googlegroups.com>.
> > For more options, visit this group at
> > http://groups.google.com/group/django-users?hl=en
> <http://groups.google.com/group/django-users?hl=en>.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/django-users/-/-hITt8lS1f0J.
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-users...@googlegroups.com.

akaariai

unread,
May 18, 2012, 12:59:40 AM5/18/12
to Django users
On May 18, 5:25 am, oneroler <samuel.ha...@gmail.com> wrote:
> Thanks Mike, that is what I was originally planning to do but realized
> there would be situations where that wouldn't do exactly what I wanted.
> For example, if there is a business that only has the strategy 'wholesale'
> assigned, using ForeignKey would still allow me to assign a different
> strategy to a division.  I was hoping to find a solution where the strategy
> for a division is constrained by the strategies assigned to its respective
> business.

Django doesn't make this particularly easy. You should create the
constraint in the database by using custom SQL, and then constraint
the assignable objects in Python in your view code. The Strategy and
Business models will create three tables into the database, one of the
tables is the many-to-many table. The m2m table's structure should be
something like this (check manage.py sqlall output):

create table business_strategy(
id serial primary key,
business_id integer references strategy(id),
strategy id integer references business(id),
unique (business_id, strategy_id)
)

Now, what you need to do is create a foreign key pointing to
business_id, strategy_id for your division table. The model should
look like this:

class Division(models.Model):
business = models.ForeignKey(Business)
name = models.CharField()
strategy = models.ForeignKey(Strategy)

this creates a table (
id serial primary key,
business_id integer references business(id),
name varchar(n),
strategy_id integer references strategy(id),
)

You will need to drop the strategy_id -> strategy(id) foreign key by
hand and add a new one: business_id, strategy_id ->
business_strategy(business_id, strategy_id). Check your database
vendors docs for details how to do this. You will want this constraint
to be deferred if available on your DB.

So, now you should have correct constraint in the database. You will
still have some problems: changing division's business changes the set
of available strategies. On UI side you will probably need to create
an AJAX call on business changed to fetch the available strategies. In
forms code you will need to do something like this:

class DivisionForm(forms.ModelForm):
class Meta:
model = Division
def __init__(self, **kwargs):
# Note, intentionally restrict the forms usage to only
kwargs.
super(DivisionForm, self).__init__(**kwargs)
business_id = None
if 'initial' in kwargs:
business_id = kwargs['initial'].business_id
if 'data' in kwargs and 'business_id' in kwargs['data']:
business_id = kwargs['data']['business_id']
self.fields['strategy'].queryset =
Strategy.objects.filter(business_set=business_id)

So, the restriction should be doable both in DB and Django, but isn't
trivial to do. The above is pseudo-code style, so the example code
will likely not work by copy-pasting...

- Anssi

oneroler

unread,
May 18, 2012, 5:36:33 PM5/18/12
to django...@googlegroups.com
Mike, Anssi,

Thank you for your replies.  I will give this a shot and see if I can get it to work.

Sam
Reply all
Reply to author
Forward
0 new messages