Fred Collins
Fred Collins

Reputation: 5010

Question about Django and User Auth

Django's noob question:

I use dango.contrib.auth for managing users of my site. But now, I'm developing the 'settings page', where an user can edit his first name, last name, and email address. But in the settings page I want also a checkbox for "newsletter".

Questions are: 1) Where should I put newsletter field in the database? 2) How can I create a form for editing these informations?

Thanks.

-- UPDATE --

Now I've this in models.py:

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique = True)
    favourite_color = models.CharField(max_length = 40)

and this in forms.py:

class UserSettingsForm(forms.ModelForm):

    class Meta:
        model = User
        exclude = ('password',)

    def save(self, commit=False):
        user = super(UserSettingsForm,self).save(commit)
        favourite_color = self.cleaned_data.get('favourite_color', '')
        if favourite_color and user.favourite_color is None:
            UserProfile(user=user).save()
        if not slug:
            UserProfile.objects.filter(user=user).delete()

        if not commit:
            user.save()
        return user

I'm a bit confused. I would edit informations like first name, last name, email and favourite color in the settings form but actually I'm doing it wrong.

Upvotes: 1

Views: 339

Answers (5)

sergzach
sergzach

Reputation: 6764

I don't know if this is a good way. But you can not to create special class for you User profile (or you have one more table in database so user search is slower).

I tried to do the next:

{ utils.py }

class Utils:

        class ClassContributor: 

            @staticmethod
            def contribute_fields( object_class, *fields ):
                for val in fields:                      
                    field_name = val[ 0 ]
                    field = val[ 1 ]
                    field.contribute_to_class( object_class, field_name )


{ models.py }

    user_contributor = Utils.ClassContributor.contribute_fields(    
        User, 
        ( 'country', models.ForeignKey( Country, null = True ) ),
        ( 'region', models.ForeignKey( Region, null = True ) ),
        ( 'city', models.ForeignKey( City, null = True ) ),
    )

In this case we have only one table in a database (problem is: added fields is not visible in django admin).

I will search a decision of the problem.

Upvotes: 0

Fred Collins
Fred Collins

Reputation: 5010

SOLVED:

this in forms.py:

    class UserProfileForm(forms.ModelForm):
        first_name = forms.CharField(label = 'First name', max_length = 40)
        last_name = forms.CharField(label = 'Last name', max_length = 40)
        email = forms.CharField(label = 'Email', max_length = 40)

        def __init__(self, *args, **kw):
            super(UserProfileForm, self).__init__(*args, **kw)
            self.fields['first_name'].initial = self.instance.user.first_name
            self.fields['last_name'].initial = self.instance.user.last_name
            self.fields['email'].initial = self.instance.user.email

        def save(self, *args, **kw):
            profile = super(UserProfileForm, self).save(*args, **kw)
            u = self.instance.user
            u.first_name = self.cleaned_data['first_name']
            u.last_name = self.cleaned_data['last_name']
            u.email = self.cleaned_data['email']
            u.save()
            return profile

        class Meta:
            model = UserProfile
            exclude = ('user', )

and in the views.py:

def settings(request):
    user = request.user.get_profile()
    if request.method == 'POST':
        form = UserProfileForm(request.POST, instance = user)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('')

    form = UserProfileForm(instance = user)
    return render(request, "settings.html", {'form': form})

Upvotes: 0

Josh Smeaton
Josh Smeaton

Reputation: 48730

Create a new model with a reference to the User model, and create a ModelForm based on the User, but includes additional fields.

class Subscription(models.Model):
    user = models.OneToOneField(User, null=True, blank=True, related_name='subscription')

class UserSettingsForm(forms.ModelForm):
    subscribed = forms.BooleanField(default=False)

    class Meta:
        model = User
        exclude = ('password',)

    def save(self, commit=False)
        user = super(UserSettingsForm,self).save(commit)
        subscribed = self.cleaned_data.get('subscribed', False)
        if subscribed and user.subscription is None:
            Subscription(user=user).save()
        if not subscribed:
            Subscription.objects.filter(user=user).delete()

        if not commit:
            user.save()
        return user

You'll also want to pass in initial data regarding subscription information to the form creation:

subscribed = user.subscription is not None
form = UserSettingsForm(instance=user, initial={subscribed=subscribed})

That should take care of it. I don't have a personal example right next to me at the moment, but that's done from memory. I'll try and update it later today if I've missed anything.

Upvotes: 0

Max Peterson
Max Peterson

Reputation: 673

You can also use inheritance and let django manage the saving.

class UserProfile(User):
    favourite_color = models.CharField(max_length = 40)

class UserProfileForm(forms.ModelForm):    
    class Meta:
        model = UserProfile
        exclude = ('password',)

This will create a one-to-one field on UserProfile pointing to User. The User fields will be saved in the User model and the other field in the UserProfile model.

Upvotes: 0

Mike DeSimone
Mike DeSimone

Reputation: 42825

You want to look at user profiles.

EDIT: Regarding the forms, is there anything stopping you from using two forms? I think Django ignores fields in the HTTP request that don't match up to a form, so you could feed the request to two forms. When rendering a template, Django doesn't generate the <form> tags or the submit button, so you just put both forms in the same <form>. Something like this in your view (modifying a Django example):

def edit_user_view(request):
    if request.method == 'POST': # If the form has been submitted...
        # Two forms bound to the POST data
        userForm = UserForm(request.POST)
        profileForm = ProfileForm(request.POST)
        if userForm.is_valid() and userForm.is_valid():
            # All validation rules pass
            # Process the data in userForm. and profileForm.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        # Unbound forms
        userForm = UserForm()
        profileForm = ProfileForm()

    return render_to_response('edit_user.html', {
        'userForm': userForm,
        'profileForm': profileForm,
    })

And in the template:

<form action="/contact/" method="post">{% csrf_token %}
{{ userForm.as_p }}
{{ profileForm.as_p }}
<input type="submit" value="Submit" />
</form>

This is really just a starting point, but I don't see any reason it can't work.

Upvotes: 6

Related Questions