James Franco James Franco - 1 year ago 38
Python Question

Cannot understand how this property on a model is being invoked

I am going over a tutorial on extending a user model and it seems to work however I had 2 questions on how a property is being invoked and about a constructor. First this is the code in question

The main model is this

class UserProfile(models.Model):
user = models.OneToOneField(User)
likes_cheese = models.BooleanField(default=True)
puppy_name = models.CharField(max_length=20)

User.profile = property(lambda u : UserProfile.objects.get_or_create(user=u)[0])--->Statement A

Now the form which gets presented to the user is this

class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('likes_cheese','puppy_name')

and the view that presents this form is as follows this is where my questions are (Over here i am simply interested in presenting the form so I removed the other code out):

def user_profile(request):
display_page = "profile.html"
csrf_token = csrf(request)
user = request.user
profile = user.profile ---------------------->statement B - Question 1 below
form = UserProfileForm(instance=profile) ------->statement C - Question 2 below
args["form"] = form
return render_to_response(display_page,args)

Now here are my two questions

Q1- Why isnt a parameter being passed to the property
object ?

from what I understand from statement A which is

User.profile = property(lambda u : UserProfile.objects.get_or_create(user=u)[0])

is that profile is being defined as a property of User and that it has a setter function which is a lambda that takes a parameter. Where is that parameter being passed ? All i see is this (A parameter to the property is not being passed anywhere that I can see)

profile = user.profile
form = UserProfileForm(instance=profile)

Q2-In the code
form = UserProfileForm(instance=profile)
we are passing in an instance as a parameter but my UserProfileForm does not have a constructor ? What is happening here.

Thanks in advance

Answer Source

Q1: Because you are using django predefined User class you have two options to add new property(to inherit User model and say django to use it for just adding new property), or just do it like you did.

When you call user.profile you already passing user as a parameter, so that lambda does UserProfile.objects.get_or_create(user=user)[0]

Q2: Your UserProfileForm inherit from forms.ModelForm which already has a constructor. And you can pass instance argument, so that form would be populated with model data(UserProfile).

django.forms.ModelForm at the same moment inherit from django.forms.models.BaseModelForm and in __init__ it checks if instance keyword argument is provided and populates form as follows, from source

if instance is None:
    # if we didn't get an instance, instantiate a new one
    self.instance = opts.model()
    object_data = {}