Django 1.4
Sorry if this is a silly question i am fairly new to Django.
I am attempting to link a user and a profile together via the inbuilt auth profile system. All the examples of this i can find do not use a class based view, which is something i would really like to use.
Basically i would like a form that combines the Profile and the User allowing me to create both at the same time. If possible i would like to use the same form to Edit/Create the User + Profile.
I have created a model for the profile: Profile
Created forms:
class UserForm(forms.ModelForm):
class Meta:
model = User
class ProfileRegisterView(FormView):
template_name = 'profile-register-form.html'
form_class = UserForm
success_url = '/account/created/'
Adding the profile to the user model does not seem to include it within the UserForm:
AUTH_PROFILE_MODULE = "creative_profile.Profile"
The 2nd alternative i have tried was to define individual forms in forms.py however the form_class attribute only accepts one form model..
Any pointers help would be great, thanks
One possible solution is to include the Profile fields in your UserForm and override the save() method to populate the Profile fields.
The save() method will have to include a get_or_create() call for the Profile model if you're not using a post_save signal to create it. If you are using a post_save signal to create the Profile model, you're going to have to make sure the User is being saved first before calling the get_profile() method.
I do it in more simple way (i suggest). Just use django build in. In urls.py I added (r'^login/$','django.contrib.auth.views.login'). In settings.py add LOGIN_URL='/login/' and to MIDDLEWARE_CLASSES add 'django.contrib.auth.middleware.AuthenticationMiddleware'. Copy registration/login.html template locally if you want to change it. After such manipulations you will have ability to login as user. Forgot, you also should import from django.contrib.auth.models User and Group.
Related
I need to add new fields to the " real Django User model ".
Why? Because I have two forms in my template with post methods in two different pages.
And I created two models, each one saves the corresponding form data in the database.
In my views I had to write two different functions for the forms pages.
The 1st one: when there is a post request, it saves the data in the 1st Model.
The 2nd one: Does the same as 1st but it saves the data in the 2nd Model.
I tried to create another new model3 which contains model1 + model2 fields (Didn't delete the previous models)! But when I submit the 1st form it saves data in an object, and when I submit the 2nd form data in another new object of model3 !
So I canceled this method and now I have to try another way to do the task, So I saved the 1st form data ( which are a login info ( email, pass ) and when a user submits the form, it creates a new user with
user = User.objects.create_user(request.POST.get("email"),request.POST.get("email"),password)
That worked fine, and now I can modify the credentials in the views with just user.username = newemail and user.password with newpassword.. but I couldn't do that with the 2nd form because the User model hasn't as much fields as the Model2! And also when I try it with
data = Model3(fielname = request.POST.get("addressLineForExample")
data.save()
it causes an error saying that I didn't specify for which user id I am going to save it!
I've seen videos and docs about the OneToOneField method! Then I figured out it just shows the model3 fields in the user page in 127.0.0.1:8000/admin, and not adding real fields to the User Model!
I came with the idea of changing user.newaddedfield = any Model3 field! I just want to realize it that way!
You can extend Django's AbstractUser to create your own custom user model on top of Django's User model. For example:
File: project/user/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
custom_field = models.BooleanField(default=False)
Then you need to run
python manage.py makemigrations
python manage.py migrate
in your terminal for creating related model in the database.
In order to see this model in your admin page, register the model using the following snippet:
File: project/user/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
P.S. I strongly recommend adding AUTH_USER_MODEL = "user.User" to project/project/settings.py in order to be able to fully use Django's default User functionalities (including Django Rest Framework's authentication ingtegrations).
P.S. Make sure you have the app's name listed under INSTALLED_APPS in your settings.py
Solution:
I went back with creating another new model3 which contains model1 + model2 fields
Then, when I submit the first form, I do:
data = Model3(field1 = request.POST.get("ValueOfThe1stFormField1")
data.save()
Now the Model3 modem will be filled with the 1st form's data but not yet with the 2nd's.
So in the 2nd function where a user should submit data in the 2nd form, I do an update to the related model3 object like:
Model3.objects.filter(field1=field1OfThe1stForm).update(field2=anything, field3=anything,...)
Hope it helps anyone who is trying to merge two (or more) models in one other model that should contain all of the previous model fields.
I've got a Django view in charge of showing a user profile. I'm using the user model provided by django itself but I also would like to extend it with some of my own information, So I made my own model to extend the user model itself:
class UserProfile(AbstractBaseUser):
is_verified = models.BooleanField(default=True)
current_profile = models.ImageField(default=static('img/default_profile.jpg'))
ratings = models.ManyToManyField(Video, through='UserProfileVideoRating', related_name='ratings')
views = models.ManyToManyField(Video, through='UserProfileVideoView', related_name='views')
CommentRating = models.ManyToManyField(Comment, through='UserProfileCommentRating', related_name='CommentRating')
subscriptions = models.ManyToManyField(User)
And here is my view I'd like to use for that:
User = get_user_model()
# Create your views here.
class profileDetailView(DetailView):
template_name = 'profile.html'
def get_object(self):
username = self.kwargs.get("username")
if username is None:
raise Http404
return get_object_or_404(User, username__iexact=username, is_active=True)
Now my question is, seeing as DetailViews are meant for a single model, How can I achieve this?
You're confused between two ways of extending the built-in User model.
If you're inheriting from AbstractBaseUser, that means you're defining your own user model - you would need to define its own username/email fields etc, and set the AUTH_USER_MODEL to point to your replacement model. In this case, you wouldn't need to reference two models in your template because your model would be the User.
However if you want to define a related UserProfile, you don't need to inherit from AbstractBaseUser, and you don't need to change AUTH_USER_MODEL; but you do need to define a relation with the actual User model, probably via a one-to-one field. In this case, to get access to the profile you just follow the relationship in the template - eg via {{ user.userprofile.currentprofile }} etc.
In your case I would advise taking the second option; remove the inheritance from AbstractBaseUser and add a one-to-one field to User.
I have some issue.
I import django User model, create new user then trying to get it profile, all what I have is "Profile matching query does not exist". Why? I just create the user.
Here is my code:
from django.contrib.auth.models import User
user = User.objects.create(username="stackoverflow", password="tester1234")
user.get_profile()
You might have forgotten to set
AUTH_PROFILE_MODULE
in your settings.py.
The online documentation suggests that get_profile()...
Returns a site-specific profile for this user. Raises django.contrib.auth.models.SiteProfileNotAvailable if the current site doesn't allow profiles, or django.core.exceptions.ObjectDoesNotExist if the user does not have a profile. For information on how to define a site-specific user profile, see the section on storing additional user information below.
Are you sure you've enabled profiles?
From your code snippet it looks like you've perhaps not created a profile which is a separate class (see here.
also write save method in signals.py:
#receiver(post_save,sender=User)
def save_profile(sender,instance,**kwargs):
instance.profile.save()
and also add this in app.py
class UserProfileConfig(AppConfig):
name = 'UserProfile'
def ready(self):
import UserProfile.signals
Django docs define this clearly, I miss that, sorry
Storing additional information about users
If you'd like to store additional information related to your users,
Django provides a method to specify a site-specific related model --
termed a "user profile" -- for this purpose.
To make use of this feature, define a model with fields for the
additional information you'd like to store, or additional methods
you'd like to have available, and also add a OneToOneField named user
from your model to the User model. This will ensure only one instance
of your model can be created for each User. For example:
from django.contrib.auth.models import User
class UserProfile(models.Model):
# This field is required.
user = models.OneToOneField(User)
# Other fields here
accepted_eula = models.BooleanField()
favorite_animal = models.CharField(max_length=20, default="Dragons.")
To indicate that this model is the user profile model for a given
site, fill in the setting AUTH_PROFILE_MODULE with a string consisting
of the following items, separated by a dot:
The name of the application (case sensitive) in which the user profile model is defined (in other words, the name which was passed to
manage.py startapp to create the application).
The name of the model (not case sensitive) class.
For example, if the profile model was a class named UserProfile and
was defined inside an application named accounts, the appropriate
setting would be:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
When a user profile model has been defined and specified in this
manner, each User object will have a method -- get_profile() -- which
returns the instance of the user profile model associated with that
User.
The method get_profile() does not create a profile if one does not
exist. You need to register a handler for the User model's
django.db.models.signals.post_save signal and, in the handler, if
created is True, create the associated user profile:
in models.py
from django.contrib.auth.models import User from
django.db.models.signals import post_save
# definition of UserProfile from above
# ...
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
I'm trying to make sure that the first name and last name field are not optional for the auth User model but I'm not sure how to change it. I can't use a sub class as I have to use the authentication system.
Two solutions I can think of are:
to put the name in the user profile but it's a little silly to have a field that I can't use correctly.
To validate in the form rather than in the model. I don't think this really fits with Django's philosophy...
For some reason I can't seem to find a way to do this online so any help is appreciated. I would have thought that this would be a popular question.
Cheers,
Durand
Simplest solution
Just create a custom UserRegisterForm which inherits the django's default UserCreationForm.
The first_name and last_name are already attributes of django's default User. If you want to make them as required fields, then recreate those fields as forms.CharField(...).
Now use your own User register form.
# Contents usersapp/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
# Inherit Django's default UserCreationForm
class UserRegisterForm(UserCreationForm):
first_name = forms.CharField(max_length=50) # Required
last_name = forms.CharField(max_length=50) # Required
# All fields you re-define here will become required fields in the form
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2']
I would definitely go with validating on the form. You could even go as far as having more form validation in the admin if you felt like it.
Thanks Mbuso for the advice. Here's my full implementation for those who are interested. Before taking a look at the source, let's see what it looks like:
I've implemented a profile model, but this will work just fine without it.
from django.core.exceptions import ValidationError
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User
from apps.profiles.models import Profile
# Define an inline admin descriptor for Profile model
# which acts a bit like a singleton
class UserProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'profile'
class MyUserChangeForm(UserChangeForm):
def clean_first_name(self):
if self.cleaned_data["first_name"].strip() == '':
raise ValidationError("First name is required.")
return self.cleaned_data["first_name"]
def clean_last_name(self):
if self.cleaned_data["last_name"].strip() == '':
raise ValidationError("Last name is required.")
return self.cleaned_data["last_name"]
# Define a new User admin
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
inlines = UserProfileInline,
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
Note: If you do implement a profile model, recommend using UserProfile as the name, since is this is what's in the documentation and seems to be the standard (this part was developed before I started working on the project). If you're using Django 1.5 or higher, skip UserProfile all together and extend the User model.
The Django way of extending the basic User model is through user profiles: see "Storing additional information about users".
If it does not fit your needs, django.contrib.auth is just a Django application, I would simply fork it. As long as you abide by the original interface, I think you will be out of trouble.
Another option is Pinax - it has OpenId support built in, you can use it with your own openid provider. OpenId native support is a battery I really miss in Django.
I have a photo field in my UserProfile model, so any user is able to upload his photo. I was thinking that all I need is ModelForm for the UserProfile, but without User specified it will fail, of course. But if I pass the request.user as a parameter into a form constructor - it won't work, too, because of the thing that form's UserProfile isn't connected with this user.
class ProfileUploadPhotoForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('photo',)
Is there any way to set, which user's profile to modify?
You need to pass it an instance of a UserProfile to update.
uploadform = ProfileUploadPhotoForm(request.POST, request.FILES,\
instance=request.user.userprofile)
if uploadform.is_valid():
uploadform.save()
Problem solved!
I forgot to add
enctype="multipart/form-data"
in form attributes.