jackofnone
jackofnone

Reputation: 25

Django ManyToManyField on multiple attributes of multiple class

Given following classes:

class Person(models.Model):
    person_id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=45)

class Person_Preference(models.Model):
    person_id = models.ForeignKey(Person, db_column='person_id')
    preference_type = models.CharField(max_length=255, blank=False)        
    preference_value = models.CharField(max_length=255, blank=False)

class Car(models.Model):
    car_id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=45)

class Car_Feature(models.Model):
    car_id = models.ForeignKey(Car, db_column='car_id')
    feature_type = models.CharField(max_length=255, blank=False)        
    feature_value = models.CharField(max_length=255, blank=False)

Tables Example

Person
---------
1 | Peter

Person_Preference
--------------------
1 | luxury | cooling
1 | safety | crash-proof
1 | luxury | heated seats

Car
----------
1 | Ford

Car_Feature
-----------------
1 | electronic | air-con
1 | verified | air-bag
1 | electronic | headed seats

Problem

I would like to create a recommendation class that link a person's preferences to car features together, i.e.:

class Recommendation(models.Model):
    preference_type = models.ManyToManyField('Person_Preference')
    preference_value = models.ManyToManyField('Person_Preference')
    feature_type = models.ManyToManyField('Car_Feature')
    feature_value = models.ManyToManyField('Car_Feature')

Tables Example

Recommendation
---------
luxury | cooling | electronic | air_con

I realized I can't do this because there are 2 attributes referencing the same class in ManyToManyField Person_Preference and also for Car_Features.

Does this mean there isn't a way to do a Many-To-Many relationship with a class that is depended on 2 attributes? Thus would the only method to resolve the above is to create a separate class for each particular preference_type and feature_type?

Updated on Oct 11, 2014

To better illustrate my problem (I am a beginner in Django, but more proficient in SQL), I am trying to write a Django version of SQL that can allow me to look up the recommendation table using preference_type as the first argument, and preference_value as the second argument:

SELECT *
FROM   Recommendation
WHERE  preference_type = 'luxury'
AND    preference_value = 'cooling'

I know the above can be achieved using Django's QuerySet filters, though I am trying to take advantage of the Many-to-many relationship because it seems (to my limited Model knowledge) this is the correct thing to do in Django. Though perhaps I am wrong about this? If anyone has a good reference to how to create Django Models then please also let me know. (I have read the official Django doc many times though I am still confused)

Thank you very much for your help.

Upvotes: 0

Views: 1449

Answers (1)

WBC
WBC

Reputation: 1914

You can have multiple M2M with the same class, you just have to add a related_name parameter like so:

class Recommendation(models.Model):
    preference_type = models.ManyToManyField('Person_Preference', related_name='foo')
    preference_value = models.ManyToManyField('Person_Preference', related_name='bar')
    ...

EDIT

For your situation, just do this:

class Recommendation(models.Model):
    #I believe convention for m2m is to pluralize the field name
    preferences = models.ManyToManyField('Person_Preference')

Your query would be:

Recommendation.objects.filter(preferences__type='luxury', preferences__value='cooling')

EDIT 2

Here is a more detailed example:

Models:

class Preference(CommonModel):
    type = models.CharField(max_length=250, null=True, blank=True)
    value = models.CharField(max_length=250, null=True, blank=True)

class Recommendation(CommonModel):
    name = models.CharField(max_length=250, null=True, blank=True)
    preferences = models.ManyToManyField(Preference, null=True, blank=True)

    def __unicode__(self):
        return self.name

In the Django shell:

p1 = Preference.objects.create(type='foo', value='bar')
p2 = Preference.objects.create(type='foo')
p3 = Preference.objects.create(value='bar')

r1 = Recommendation.objects.create(name='foobar')
r1.preferences.add(p1)
r2 = Recommendation.objects.create(name='foo')
r2.preferences.add(p2)
r3 = Recommendation.objects.create(name='bar')
r3.preferences.add(p3)

>>> Recommendation.objects.filter(preferences__type='foo')
[<Recommendation: foobar>, <Recommendation: foo>]

>>> Recommendation.objects.filter(preferences__value='bar')
[<Recommendation: foobar>, <Recommendation: bar>]

>>> Recommendation.objects.filter(preferences__type='foo', preferences__value='bar')
[<Recommendation: foobar>]

Upvotes: 1

Related Questions