views:

118

answers:

1

Let's say that I have a Person who runs an inventory system. Each Person has some Cars, and each Car has a very large number of Parts (thousands, let's say).

A Person, Bob, uses a Django form to create a Car. Now, Bob goes to create some Parts. It is only at the form level that Django knows that the Parts belong to some specific Car, and that the Parts.ForeignKey(Car) field should only have a specific Car as a choice. When creating a Part, you have to mess with the form's constructor or similar in order to limit the choice of Cars to only the cars owned by Bob.

It does not seem at all proper or secure to enforce this ownership at the form level. First, it seems that other users' Cars must be inaccessible to anyone but the owner of the Car; currently, only solid form programming prevents anyone from viewing any other Person's Cars! Second, it is seems sloppy to take care of the problem by modifying constructors in this manner. What do you all think about this, and is there any way to enforce this?

+1  A: 

Hi, first of all part of this should happen at a level where you know who is the current user. In Django theres nothing like a global request.user, so you need to check for this where you have a request object or pass it as a paremeter.

One way of achieving what you ask for is to use a custom manager. For the Car class you could use

class CarManager(models.Manager):
    def for_user(self, user):
        return super(CarManager, self).get_query_set().filter(creator=user)

You might also want to override the default manager and it's default methods to only allow this method.

For the second part, you don't need to modify the constructor there are other ways if you don't like it. But modifying them assures you for example you can't create a form without a logged user. Another way is to use a ModelChoiceField and override it's queryset attr in the view:

form.car.queryset = Car.objects.for_user(request.user)

but this approach is error prone. You should sitck to modifying the form constructor as this:

class PartForm(forms.ModelForm):
    def __init__(self,user,*args,**kwargs):
        super (PartForm,self ).__init__(*args,**kwargs)
        self.fields['car'].queryset = Car.objects.for_user(user)

Hope this helps.

rasca