Search code examples
pythondjangodjango-users

How to create a UserProfile field which is alias for a User field?


I want access User model's first_name, last_name and email fields from UserProfile as if they were UserProfile's own fields.

class Person(models.Model):
    user = models.OneToOneField(User)
    name = #ModelField which refers to user.first_name
    surname = #ModelField which refers to user.last_name
    email = #ModelField which refers to user.email
    birth_date = models.DateField(null=True, blank=True)
    #and other fields

I could use independent name, surname, email, fields but it would have caused data duplication with User fields.

upd

I want name, surname and email to be of some Field type (as birth_date) and modification of these fields to equal modification of corresponding User fields (property behavior shown below). I need this because I want these three fields to be edible in admin interface or to be prcocessed equally with "native" fields in ModelForm for example.

upd solution. Not very fancy, but here how it worked for me:

class Person(models.Model):
    user = models.OneToOneField(User)
    #and also some char, text fields

    @property
    def name(self):
        return self.user.first_name
    @name.setter
    def name(self, value):
        self.user.first_name = value

    #and by analogy surname and email properties


class PersonForm(ModelForm):
    class Meta:
        model = Person
        exclude = ('user',)

    name = forms.CharField(max_length=100, required=False)
    surname = forms.CharField(max_length=100, required=False)
    email = forms.EmailField(required=False)

    #no make fields filled with User data on form load
    def __init__(self, *args, **kwargs):
    if 'instance' in kwargs:
        instance = kwargs['instance']
        initial = kwargs.get('initial', {})
        initial['name'] = instance.name
        initial['surname'] = instance.surname
        initial['email'] = instance.email
        kwargs['initial'] = initial
    super(PersonForm, self).__init__(*args, **kwargs)

    # to save form data to User model when commit
    def save(self, commit=True):
        instance = super(PersonForm, self).save(commit)  
        user = instance.user
        user.first_name = self.cleaned_data['name']
        user.last_name = self.cleaned_data['surname']
        user.email = self.cleaned_data['email']
        user.save()
        return instance

class PersonAdmin(admin.ModelAdmin):
    fields = ['name', 'surname', 'email', 'and_others']

    form = PersonForm

admin.site.register(Person, PersonAdmin)

Solution

  • If want you want is to access user.first_name, user.last_name, etc... directly as attributes of Person, you can add a name, surname, etc... properties to Person model, instead of fields:

    class Person(models.Model):
        user = models.OneToOneField(User)
        birth_date = models.DateField(null=True, blank=True)
        #and other fields
    
        @property
        def name(self):
            return self.user.first_name
    
        @property
        def surname(self):
            return self.user.last_name
        ....