Russell Keith-Magee wrote: ... > That said, 'm2m with intermediate' is a relatively common use case, so > if you have any neat ideas on how to represent such a structure, feel > free to suggest them. The idea has been discussed before, but no > obvious solution has emerged (the real sticking point is querying the > intermediate table - check the archives for previous discussions).
Here's some brainstorming. This post is long, and it's all daydreaming. I suspect someone else may have already come up with the same answer and it doesn't work for some reason... Checked the archives, found some relevant discussions, but not sure I found the right ones :-/ The problem is, how to add some attributes to a ManyToMany relation- how to define them and use them. Django currently hides the intermediate table, and that's good. It can stay hidden for the usual use case, where all you want is a many-to-many relationship. I think, when someone wants to add "fields" to the relationship, then it should become visible, and also synthesize models with both relationship attributes and table behavior. How would it look? Starting with defining half, the inner Meta class seems like a good place to go: class Reporter(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) class WritingRole(models.Model): responsibility = models.CharField(maxlength=30) description = models.CharField(maxlength=30) pay_scale = models.ForeignKey(PayScale) class Article(models.Model): headline = models.CharField(maxlength=100) pub_date = models.DateField() writers = models.ManyToManyField(Reporter) # Here's a stab at 'm2m with intermediate' defining syntax class Meta: writers.actual_pay=models.SmallIntegerField() # We don't pay much! writers.writing_role=models.ForeignKey(WritingRole) That's it. Looks pretty clean, is easy to understand. Maybe the "writers.<fields>' don't even go in the Meta subclass, maybe they stay in the main class. Recursion requires a little thought. # 'self' should always mean 'this model',Article in this case writers.similar_articles=models.ForiegnKey('self') # so spell out when you really want to self-relate writers.collusion_group=models.ForeignKey('Article.writers') # And someone will find a need for ManyToMany recursion! How to use? The old API doesn't change, things work as always art = Article.objects.get(pk=3) writers = art.writers.all() # as usual, gives all reporters writer1 = reps[1] # get a single reporter from the list print writer1.last_name # "Smith" written_things = writer1.article_set.all() # find all articles for the reporter print written_things[0].headline # "Django Gets Useful Model Enhancements" Maybe you're wondering why I didn't call the variables "reporter" and "article". I'm thinking that when you define "writers"- when you define a relationship with attributes- you're saying that the model on the other side has something extra, when viewed from this side. So, you get a subclass of the foreign model.* It does all the things that the related class does, plus lets you retreive the attributes for that particular relation: writer1.actual_pay # 0 if working for free! writer1.writing_role.responsibility # "Muse"... # describe the role writer1 had when writing this article written_things[0].writing_role.description Given a plain old Reporter object "reporter1": reporter1.writers_set.all() # gets all Article-plus objects for writer reporter1.writers_set.get(headline='Foo') # may have the syntax a bit off here- find Article-plus object with headline 'Foo' reporter1.writers_set.filter(writingrole__responsibility='Author') # find Article-pluses for which reporter1 was the author. * I realize this abstraction my be contentious- if you ask for a Reporter you should get a Reporter and not some subclass Django creates for you. That's my reaction too. It does seem cleaner to make a 'Writers' object of its own that behaves like a model with two foreign key fields, and the extra attributes. Indeed Django should, so one can make requests like: Writers.objects.filter(reporter__last_name='Smith',actual_pay=0).count() # how often do people named Smith work for free? But then how would that abstraction work for other common cases? reps = art.writers.all() # really gives Reporters this time rep1 = reps[1] # get a single reporter from the list # We've lost the information we got traversing the relation! rep1.actual_pay # gives an attribute error art.writers.actual_pay # what writer are we talking about? art.writers[1].actual pay # huh? Even if that syntax works to select a single writer, 'art.writers' returns Reporters, which don't have "actual_pay" Writers.objects.get(article=art,reporter=rep1).actual_pay # This works. I think it hits the database again. So, you can get something working with an auto-created class named after the M2M relation (with an override to let user give it a different name, perhaps). I do think that traversing such a "relationship-with-extras" should give a subclass of the related object: something that has behaviors and data from the related class, and also the relationsip-with-attribute-class. It makes the app writers job easier, and it doesn't break existing code, since existing code doesn't have "relationship-with-extras" yet. ("relationship-with-extras" is fun!) --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to django-users@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/django-users -~----------~----~----~----~------~----~------~--~---