Eliminating multiple inheritance - python

I have the following problem and I'm wondering if there's a nice way to model these objects without using multiple inheritance. If it makes any difference, I am using Python.
Students need contact information plus student information. Adults need contact information plus billing information. Students can be adult students, in which case I need contact/student/billing info, or they can be children, in which case I need contact/student/parent info.
Just to be clear on how the system will be used, I need to be able to ask for a list of all adults (and I will get adult students plus parents), or a list of all students (and I will get child students plus adult students).
Also, all of these objects need to have a common base class.

What you have is an example of Role -- it's a common trap to model Role by inheritance, but Roles can change, and changing an object's inheritance structure (even in languages where it's possible, like Python) is not recommended. Children grow and become adults, and some adults will also be parents of children students as well as adult students themselves -- they might then drop either role but need to keep the other (their child changes schools but they don't, or viceversa).
Just have a class Person with mandatory fields and optional ones, and the latter, representing Roles, can change. "Asking for a list" (quite independently of inheritance or otherwise) can be done either by building the list on the fly (walking through all objects to check for each whether it meets requirements) or maintaining lists corresponding to the possible requirements (or a mix of the two strategies for both frequent and ad-hoc queries). A database of some sort is likely to help here (and most DBs work much better without inheritance in the way;-).

As I'm sure someone else will comment soon (if they haven't already), one good OO principle is "Favor composition over inheritance". From your description, it sounds suspiciously like you're breaking the Single Responsibility Principle, and should be breaking down the functionality into separate objects.
It also occurs to me that Python supports duck typing, which begs the question "Why is it so important that all the classes have a common base class?"

Very simple solution: Use composition rather than inheritance. Rather than having Student inherit from Contact and Billing, make Contact a field/attribute of Person and inherit from that. Make Billing a field of Student. Make Parent a self-reference field of Person.

It doesn't sound like you really need multiple inheritance. In fact, you don't ever really need multiple inheritance. It's just a question of whether multiple inheritance simplifies things (which I couldn't see as being the case here).
I would create a Person class that has all the code that the adult and student would share. Then, you can have an Adult class that has all of the things that only the adult needs and a Child class that has the code only the child needs.

This sounds like something that could be done quite nicely and flexibly with a component architecture, like zope.components. Components are in a way a sort of super-flexible composition patterns.
In this case I'd probably end up doing something when you load the data to also set marker interfaces on it depending on some information, like if age >= 18 you set the IAdult interface, etc. You can then get the adult information by doing
adultschema = IAdultSchema(person)
or something like that.
(Edit: Actually I'd probably use
queryAdapters(person, ISchema)
to get all schemas in one go. :)
A component architecture may be overkill, but once you got used to thinking like that, many problems get trivial. :)
Check out Brandons excellent PyCon talk about it: http://www.youtube.com/watch?v=UF77e2TeeQo
And my intro blog post: http://regebro.wordpress.com/2007/11/16/a-python-component-architecture/

I think your requirements are over-simplified, since in a real situation, you might have students with their own accounts to handle billing even if they are minors who need parent contact information. Also, you might have parental contact information be different from billing information in an actual situation. You might also have adult students with someone else to bill. BUT, that aside - looking at your requirements, here is one way:
classes: Person, BillingInfo, StudentInfo.
All people are instances of class Person...
class Person:
# Will have contact fields all people have - or you could split these off into an
# object.
parent # Will be set to None for adults or else point to their parent's
# Person object.
billing_info # Set to None for non-adults, else to their BillingInfo object.
student_info # Set to None for non-student parents, else to their StudentInfo
# object.
Checking the fields will allow you to create lists as you desire.

One solution is to create a base Info class/interface that the classes ContactInfo, StudentInfo, and BillingInfo inherit from. Have some sort of Person object that contains a list of Info objects, and then you can populate the list of Info objects with ContactInfo, StudentInfo, etc.

In pseudocode, you could do something like this:
Class Student
Inherits WhateverBase
Private m_StudentType as EnumStudentTypes 'an enum containing: Adult, Child
Private m_Billing as Billing
Private m_Contact as Contact
Private m_Parent as Parent
Public Sub Constructor(studentType, billing, contact, parent)
...logic to make sure we have the right combination depending on studentType.
...throw an exception if we try to assign a a parent to an adult, etc.
...maybe you could have seperate constructors, one for each studenttype.
End Sub
Public Property StudentType as EnumStudentTypes
Get
Return m_StudentType
End Get
End Sub
Public Property Parent
Get
...code to make sure we're using a studentType that has a parent,
...and throws an exception if not. Otherwise it returns m_Parent
End Get
End Sub
[more properties]
End Class Student
Then you could create a class called StudentManager:
Public Class StudentManager
Public Function GetAdults(studentCollection(Of Students)) as StudentCollection(Of Students)
Dim ResultCollection(Of Students)
...Loop through studentCollection, adding all students where Student.StudentType=Adult
Return ResultCollection
End Function
[Other Functions]
End Class
Public Enum StudentType
Adult=0
Child=1
End Enum

Related

Visitor Pattern, why is it useful?

I used the Visitor example given here Where we have this:
.------------------------.
| Flower |
+------------------------+
| +accept(visitor) |
| +pollinate(pollinator) |
| +eat(eater) |
'------------------------'
We also have a Bug and a Bee that can pollinate a Flower, and a Predator that can eat a flower.
Using the vistor pattern I can write this:
bee = Bee()
fly = Fly()
worm = Worm()
# Using the visitor pattern:
for flower in flowerGen(10):
for object in [bee, fly, worm]:
flower.accept(object)
But the code is as readable and funcitonal without the visitor:
# Without visitor pattern
for flower in flowerGen(10):
for object in [bee, fly, worm]:
object.visit(flower)
The question is, what advantages provide the Visitor Pattern in this example?
The article you link to is pretty clear as to why you'd want to use a visitor pattern: when you can't alter the objects because they come from a third party:
The assumption is that you have a primary class hierarchy that is fixed; perhaps it’s from another vendor and you can’t make changes to that hierarchy. However, your intent is that you’d like to add new polymorphic methods to that hierarchy, which means that normally you’d have to add something to the base class interface. So the dilemma is that you need to add methods to the base class, but you can’t touch the base class. How do you get around this?
Sure, if you can just add a visit method to bees, flies and worms, then that's fine. But when you can't, using the visitor pattern is the next best option.
Note that in the article the relationship is reversed; you can't alter the Flower hierarchy:
# The Flower hierarchy cannot be changed:
but the class does support the visitor dispatch pattern via the visit method:
class Flower(object):
def accept(self, visitor):
visitor.visit(self)
That implementation could be much more complex; the example has been simplified down to a simple visitor.visit() call here, but in practice a real visitor pattern can and does do much more at this stage.
For example, there could be composite classes, which contain multiple subcomponents. The accept() method would then delegate further down to those sub-elements to then call accept on them all, as needed. Keeping with the flower theme, perhaps there's a Chrysanthemum or Dahlia class, where some visitors would eat the ray components, while others would like to visit the components in the eye to pollinate. It's up to the composite object to direct each visitor to those parts individually.
If you are looking for specific examples, take a look at the ast module, which offers a NodeVisitor class which should be subclassed to add methods to let you customise how the AST tree passed in is being processed. I've used the specific NodeTransformer subclass to alter how Python code works on several occasions. Here the visitor pattern is used to effectively filter out certain types in a larger hierarchy, greatly simplifying AST-handling code without having to alter any of the AST node classes themselves.

Sorted/searchable StackedInline options

I have three classes:
Person
Publication
AuthorOrder
Publication has a m2m field to Person through AuthorOrder. This is done to enable making it possible to set the order of authors, which matters for academic publications.
However, the list of Persons is getting long, and it would therefore be nice if it was searchable or at least sorted. However, if I use the obvious method of setting a Meta class to Person, this causes the Persons to be sorted everywhere, including where I don't want them to be.
Is there a way to make it searchable (best) or at least sorted without ruining the order elsewhere (ok)?
Looks like this:
There you go. This works well for us on several projects. https://github.com/and3rson/django-searchable-select

Number of attributes in Django Models

I searched a lot and did not find what I´am looking for.
What would be the best concept for a model class in django?
To extend User, would be better to have a class with several attributes, or break this class into several classes with few attributes? I´m using the django ORM now.
Say I have a class called Person that extends User, would be better:
class Person(models.Model):
user = foreingkey(User)
attribute1 =
...
attributeN =
Or, would it be better to do this:
class PersonContac(models.Model):
user = foreingkey(User)
attribute1 =
...
attribute3 =
class PersonAddress(models.Model):
user = foreingkey(User)
attribute1 =
...
attribute3 =
class PersonHobby(models.Model):
user = foreingkey(User)
attribute1 =
...
attribute3 =
My each of my views would use the data from the smaller classes (probably).
Over time, the atrribute number can expand.
I want to do is do it once, and touch the minimum possible.
Various attributes can be unfilled by the user, they are not required.
The number of user is indefinite (can be a lot).
I´m concerned in terms of long term performance and maintaining.
If someone can explain me, what would be better for my code, and why.
And what would be better in general (less classes/more attributes, or more classes/less attributes), using the Django ORM.
It is better if my views use the data of only one model class, or it makes no (or little) difference?
Edit:
On the rush for writing I used bad names on class. None of these attributes are many-to-many fields, the User will have only one value for each attribute, or blank.
The number of atributes can expand over time, but not in a great number.
Put any data that is specific to only one User directly in the model. This would probably be things like "Name", "Birthday", etc.
Some things might be better served by a separate model, though. For example multiple people might have the same Hobby or one User might have multiple Hobby(s). Make this a separate class and use a ForeignKeyField or ManyToManyField as necessary.
Whatever you choose, the real trick is to optimize the number of database queries. The django-debug-toolbar is helpful here.
Splitting up your models would by default result in multiple database queries, so make sure to read up on select related to condense that down to one.
Also take a look at the defer method when retrieving a queryset. You can exclude some of those fields that aren't necessary if you know you won't use them in a particular view.
I think it's all up to your interface.
If you have to expose ALL data for a user in a single page and you have a single, large model you will end up with a single sql join instead of one for each smaller table.
Conversely, if you just need a few of these attributes, you might obtain a small performance gain in memory usage if you join the user table with a smaller one because you don't have to load a lot of attributes that aren't going to be used (though this might be mitigated through values (documentation here)
Also, if your attributes are not mandatory, you should at least have an idea of how many attributes are going to be filled. Having a large table of almost empty records could be a waste of space. Maybe a problem, maybe not. It depends on your hw resources.
Lastly, if you really think that your attributes can expand a lot, you could try the EAV approach.

Is adding attributes dynamically frowned upon in Python?

In Python, you can assign an arbitrary attribute from outside the defining class:
class Profile(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=140)
p = Profile()
p.age = 42
The underlying mechanism here is __dict__ attribute that maintains a dictionary of all attributes.
We were all told not to expose our inner workings to the client code, but attaching new data doesn't have to do with encapsulation at all, right? Is this idiom common for Python code?
Just What I Mean…
Each Tweet has standard fields, like id, text, owner.
When returning tweet list for a user, you want to display if a tweet is “favorited” by this user.
Obviously, to obtain is_favorite you need to query many-to-many relationship for this user.
Would it be OK to pre-fill Tweet objects with is_favorite corresponding to current user?
Sure I could expose a method is_favorite_for(user) but I'm hitting Django template language limitations that doesn't allow to call methods with arguments from inside the template. Also, I believe a template should not be calling methods at all.
I know this will work fine, but I wonder if doing something like that in an open source project would get other developers to look on me with contempt.
Sidenote:
I come from C#/.NET background where dynamic types were introduced very recently and aren't adapted widely except for some niche areas (interoperability, IoC frameworks, REST client frameworks, etc).
My view is that it is a bad practice.
The object doesn't know that you're messing with its attributes. Consider, for example, what would happen if Profile were later expanded to have an attribute called age, unrelated to p.age in your code.
If you want to add attributes, why not subclass Profile, or have an external mapping of Profiles to an object with your custom attributes?
I think the answer is: It depends. First, if you really want to prevent it you can by defining __slots__ in the class. And it is not generally a good practice to add attributes not actually defined in the class, as it can be confusing to someone reading
the code and is rarely useful.
But at certain times, it is useful to be able to do this and Python documentation discusses this as a way to get something similar to a C struct or Pascal Record (see http://docs.python.org/tutorial/classes.html under section 9.7 Odds and Ends.)
If the attribute is only there sometimes, you risk getting an AttributeError out of nowhere for one object while the code worked fine for another object of the same class (yes, exact types aren't that important when duck-typing, but objects of the same class are frequently assumed to be of the same "duck type"). Even if it doesn't happen, you can't be sure just by looking at part of the code, and it's much harder to check in any case. So, doing this only makes your code less reliable.
Then there's the option of providing a default attribute as class attribute or property, only assigning an object attribute when it differs from the default. But for stuff that is expected to vary per object, the clarity of having every attribute ever listed in __init__ usually outweights any potential advantages of delaying instance attribute access.
That is not to say it's not acceptable, but you'd have to make a compelling argument for it to be considered a good idea.

Does Django support model classes that inherit after many non-abstract models?

Lets say I have three django model classes - lets call them A, B and C. If A and B are abstract, I can do something like:
class C(A,B):
pass
What if they aren't abstract and I do the same? Will everything still work correctly or no? Or have I got it wrong and this should not be done with abstract models either?
I'm having some issues which I'm attributing to the fact that the answer is probably no, but I'd still prefer to make sure about this if anyone knows :)
The specific use case I had for this is probably better served by Generic Relations (I only recently discovered their existence), so I guess it would be understandable if the Django team made a design decision like this (I can't see many people needing to do this). I'd just like to know for sure what the case is.
Edit 1 (after Dominic's answer)
Interesting... The problem we're having is a structure similar to IMDb (I think IMDb is a bit easier to understand than the topic matter we actually have, so I'll use them as an example). On IMDb they have pages for People and pages for Movies and both People and Movies have their own message boards.
We've ended up connecting message boards to the People and Movies by creating a model called MessageboardOwner (with only one attribute - the id added automatically by Django), which "owns" the message board and People and Movies inherit it. The problem is that our "People" class inherits from two other classes also. The class definition is something like:
class Person(A,B,MessageboardOwner):
Initially this seemed to work out fine, but then today something rather weird happened... I was deleting a Person in the admin and the admin asked the "Are you sure?" question and was showing me what other objects it would have to delete. It was trying to delete two message boards, not one. One of these message boards should have been owned by a Movie, not a Person.
Upon looking at what exactly was in the database, I found that this Person instance was using the same MessageboardOwner instance as the Movie was. When I played around with it, what came out was that the Movie class, which inherited only after MessageboardOwner, seemed to work ok. Saving the Person, however, only created a MessageboardOwner object if one didn't already exist (or possibly overwrote the existing one - I'm not sure). I also found that the id fields inherited from A, B and MessageboardOwner were always equal, which seemed strange to me.
Yes, you can use normal Python multiple-inheritance with models. Bear in mind this warning though:
Just as with Python's subclassing,
it's possible for a Django model to
inherit from multiple parent models.
Keep in mind that normal Python name
resolution rules apply. The first base
class that a particular name (e.g.
Meta) appears in will be the one that
is used; for example, this means that
if multiple parents contain a Meta
class, only the first one is going to
be used, and all others will be
ignored.
Generally, you won't need to inherit
from multiple parents. The main
use-case where this is useful is for
"mix-in" classes: adding a particular
extra field or method to every class
that inherits the mix-in. Try to keep
your inheritance hierarchies as simple
and straightforward as possible so
that you won't have to struggle to
work out where a particular piece of
information is coming from.
From the Django docs.
Generally, multiple inheritance is a bad idea, and there are simpler ways to do things. If you flesh out what problem you're trying to solve a bit more clearly, we might be able to help a bit better.

Categories

Resources