i'm using Django registration, and unlike everybody else, I seem to have the opposite problem. My User object is saved fine, but my UserProfile object isn't!
I followed this website:
http://birdhouse.org/blog/2009/06/27/django-profiles/
which was really good, and so now i have:
class ProfileForm(forms.ModelForm):
YESNO = [
(True,mark_safe('<img src="/static_files/greenTick.png"/>')),
(False,mark_safe('<img src="/static_files/redCross.png"/>'))]
class Meta:
model = UserProfile
exclude = ('isTweeting','points','user')
fields = ('display_name','first_name','last_name','email','gravatar')
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
self.fields['email'].initial = self.instance.user.email
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
self.fields['display_name'].initial = self.instance.user.username
self.fields['gravatar'].initial = self.instance.usesGravatar
#add in the input to size it juuuuust right.
email = forms.EmailField(label="Primary email",help_text='',widget=forms.TextInput(attrs={'class': 'wideInput'}))
first_name = forms.Field(label="First name",help_text='',required=False,widget=forms.TextInput(attrs={'class': 'wideInput'}))
last_name = forms.Field(label="Last name",help_text='',required=False,widget=forms.TextInput(attrs={'class': 'wideInput'}))
display_name = forms.Field(label="Display name",help_text='',widget=forms.TextInput(attrs={'class': 'wideInput'}))
gravatar = ImgModelChoiceField(label='Gravatar', choices=YESNO, widget=forms.RadioSelect(renderer=ImgRadioFieldRenderer))
def save(self, *args, **kwargs):
"""
Update the primary email address on the related User object as well.
"""
u = self.instance.user
u.email = self.cleaned_data['email']
u.username = self.cleaned_data['display_name']
u.first_name = self.cleaned_data['first_name']
u.last_name = self.cleaned_data['last_name']
u.save()
self.instance.gravatar = (self.cleaned_data['gravatar'] == 'True')
profile = super(ProfileForm, self).save(*args,**kwargs)
return profile
this object is passed into the Django-profile as the form_class for use, as described in the website above. The problem i have is that when i submit my form, while the "User" data is updated correctly - any changes in the email or whatnot propagate to the db - the change to the "gravatar" value is not sent. Also no error is thrown.
Any ideas what I should do?
I'm going to hazard a guess here
def save(self, *args, **kwargs):
...
self.instance.gravatar = (self.cleaned_data['gravatar'] == 'True')
profile = super(ProfileForm, self).save(*args,**kwargs)
return profile
It seems you are using a custom widget, and by the looks of things you need to change the string 'True' (passed back from the form) to a boolean True before saving it to the DB. When you call save() on the next line though, the ModelForm will overwrite the value you have given self.instance.gravatar with the data directly from the form's cleaned_data:
https://github.com/django/django/blob/master/django/forms/models.py#L351
Also, in __init__, you don't need to include
self.fields['gravatar'].initial = self.instance.usesGravatar
as this field is already bound to the model form and will be automatically populated (if the UserProfile is being edited for example) when you instantiate the form along with an instance in your view.
Finally, in your Meta, you don't need to include both excludes and fields, one or the other should be fine.
First of all consider suggestions from #Timmy.
The only thing which else should be noticed is in this line:
profile = super(ProfileForm, self).save(*args,**kwargs)
By default the save method has commit=True. Verify that the function which is calling this Form might be sending commit=False in args or kwargs. If yes then you have to manually save the profile profile.save() before returning because commit=False means the changes will not reflect to the db.
And why you are allowing user to update both username and email? How you will keep track of the registration process if you are allowing to update both fields? Usually user sign up using their email. Define your criteria which field(username or email) you want to kept unchanged.
Update
Also you are doing one more thing wrong in your save function. You are updating the email, username, firstname and lastname in user taken from instance.user. But that instance overwritten when profile form default save is call here profile = super(ProfileForm, self).save(*args,**kwargs). What you should do is to update those fields using the user = profile.user The profile which is returned by the super. Your save function should be looked like this:
def save(self, *args, **kwargs):
"""
Update the primary email address on the related User object as well.
"""
profile = super(ProfileForm, self).save(*args,**kwargs)
u = profile.user
u.email = self.cleaned_data['email']
u.username = self.cleaned_data['display_name']
u.first_name = self.cleaned_data['first_name']
u.last_name = self.cleaned_data['last_name']
u.save()
#profile.save() #if commit=False in kwargs
return profile
Related
Here i am simply using User model from django.contrib.auth.models import User and I have a custom userprofile model where user foreign key in that django built in User model , well i have created an email_address field which can manually update by a user, and there is an other email field which is built in inside django User model , I want to update that email field as userprofile email_address field update.
I am simply getting user object which username and try to get email of that user object but getting an error : 'QuerySet' object has no attribute 'email'
models.py
def save(self,*args, **kwargs):
user_obj = User.objects.filter(username=self.user.username)
print(user_obj,'user object')
print(user_obj.email,'email have a user')
email = self.email_address
user_obj.email = self.email_address
print(user_obj.email,'email have a user')
user_obj.save(user_obj.email)
super(UserProfile,self).save(*args, **kwargs)
user_obj is not a User object, it is a QuerySet of User objects.
You can retrieve a single User object with .get(…) [Django-doc]:
def save(self,*args, **kwargs):
user_obj = User.objects.get(username=self.user.username)
user_obj.email = self.email_address
user_obj.save()
super(UserProfile,self).save(*args, **kwargs)
But here you can actually simply use the self.user object:
def save(self,*args, **kwargs):
user = self.user
user.email = self.email_address
user.save()
super(UserProfile,self).save(*args, **kwargs)
That being said, I would advise not to store data twice. So in case the User object has an email address, there is no need to store this in the UserProfile model as well. You can easily access this with:
from django.conf import settings
class UserProfile(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
#property
def email_address(self):
return self.user.email
This thus means that you access the email address of the related User object. This avoids data duplication, which will make it hard to keep data in sync. Especially since the .save() method will not run for example for bulk create/updates by the Django ORM.
So I been seeing the same question posted in different scenarios and I am unable to get mine to work. Basically trying to find a model instance if it already exists and create a new one if it doesn't - based on if the instance have the same field as the user's username
I tried get_object_or_404 and even changing the primary key to a field in the model class.
this is the models.py
class Cart(models.Model):
user = models.CharField(max_length=30)
#classmethod
def create(cls, user):
user = cls(user=user)
return user
def __str__(self):
"""String for representing the Model object."""
return f'{self.id} {self.user}'
this is the views.py
def cart(request, pk):
try:
pizza = PizzaInstance.objects.get(id=pk)
# get the topping(s)
topping_1 = int(request.POST["topping1"])
topping = PizzaTopping.objects.get(pk=topping_1)
# get the username
user = request.user.username
# check if the user already has an order
try:
order = Cart.objects.get(user=user)
except KeyError:
order = Cart.create([user])
order.save()
user creation
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'password1',
'password2'
)
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
I expected to see the order being saved and a new instance being created or even new instances added to the cart.
error is get is -
Cart matching query does not exist.
Thank you!
There is a couple of problems:
You should retrieve the user with request.user instead of request.user.username (the latter gives you the username which is a str instead of the User instance.
You shouldn't really create your objects with a custom class method. Instead, use the object manager and call Cart.objects.create(user=user) (and if you do need to make a custom create() function, it should be defined on a custom manager class).
With these changes you should be able to use Cart.objects.get_or_create(user=user), which returns a tuple containing your Cart object and a bool indicating wether the object was created or not (ie. existed in the first place).
So put together:
def cart(request, pk):
try:
pizza = PizzaInstance.objects.get(id=pk)
# get the topping(s)
topping_1 = int(request.POST["topping1"])
topping = PizzaTopping.objects.get(pk=topping_1)
# get the user
user = request.user
# get the Cart object associated with 'user' or create a new one
order, created = Cart.objects.get_or_create(user=user)
Try using the .get_or_create() method:
cart, created = Cart.objects.get_or_create(...)
You're doing some odd things here.
Cart.objects.get won't raise a KeyError. It raises the error you see: Cart.DoesNotExist. So that is the error you need to catch.
Additionally, for some reason you are wrapping the user in a list when you pass it in the except block. Don't do that.
try:
order = Cart.objects.get(user=user)
except Cart.DoesNotExist:
order = Cart.create(user)
order.save()
I am using Flask-Admin for my Flask-based project. In it, I have some models (using peewee) where the primary-key is user-set, such as username for a User. However Flask-Admin is not showing these fields in the model's create/edit pages.
Now, when I try to create a new user, the "Save" button gives a peewee.UserDoesNotExist error, and the "Save & Add" says "Record successfully created" twice but doesn't actually do anything.
I had extended the save() method to auto-generate the username from the name if it's unset, but the problem persisted even when I removed the overriding.
The code...
Here's what my User model looks like:
# import peewee as pw
class User(BaseModel, UserMixin):
username = pw.CharField(32, primary_key=True)
password = pw.CharField(512, null=True)
name = pw.CharField(64)
# ... other fields not shown ... #
def save(self, *args, **kwargs):
# Set the username if field is blank
if self.username == 'auto' or not self.username:
self.username = self.name.replace(' ', '').lower()
# Do the real save
super(User, self).save(*args, **kwargs)
Here's my admin code:
# from flask_admin.contrib.peewee.view import ModelView
class AdminModelUser(ModelView):
can_create = True
column_list = ('username', 'name', 'group', 'active')
admin.add_view(AdminModelUser(User, name='Users', category='Accounts'))
Trying out stuff
I later tried to override the get_form() method, to use wtfpeewee directly and allow the pk, like this:
# from wtfpeewee.orm import model_form
class AdminModelUser(ModelView):
...
def get_form(self):
return model_form(User, allow_pk=True)
Now the field is showing, but saving still does not work. When I edit the username of an existing user, the admin says "Record was successfully saved", but it doesn't get saved. And when I try to create a new user, I still get a peewee.UserDoesNotExist error.
My guess is that I've done the overriding in the wrong place, with the fields showing in the form but not in the save methods. I couldn't find any mention of this in the docs: does anyone know how to do it?
When you've got a non-integer primary key, you must call save() with force_insert=True to add a new row.
http://docs.peewee-orm.com/en/latest/peewee/models.html#non-integer-primary-keys-composite-keys-and-other-tricks
I am trying to force users to enter their email when they sign up. I understand how to use form fields in general with ModelForms. I am unable to figure out how to force an existing field to be required, however.
I have the following ModelForm:
class RegistrationForm(UserCreationForm):
"""Provide a view for creating users with only the requisite fields."""
class Meta:
model = User
# Note that password is taken care of for us by auth's UserCreationForm.
fields = ('username', 'email')
I am using the following View to process my data. I am not sure how relevant it is, but it is worth saying that other fields (username, password) are properly loading with errors. The User model already has those fields set as required, however.
def register(request):
"""Use a RegistrationForm to render a form that can be used to register a
new user. If there is POST data, the user has tried to submit data.
Therefore, validate and either redirect (success) or reload with errors
(failure). Otherwise, load a blank creation form.
"""
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
# #NOTE This can go in once I'm using the messages framework.
# messages.info(request, "Thank you for registering! You are now logged in.")
new_user = authenticate(username=request.POST['username'],
password=request.POST['password1'])
login(request, new_user)
return HttpResponseRedirect(reverse('home'))
else:
form = RegistrationForm()
# By now, the form is either invalid, or a blank for is rendered. If
# invalid, the form will sent errors to render and the old POST data.
return render_to_response('registration/join.html', { 'form':form },
context_instance=RequestContext(request))
I have tried creating an email field in the RegistrationForm, but this seems to have no effect. Do I need to extend the User model and there override the email field? Are there any other options?
Thanks,
ParagonRG
Just override the __init__ to make the email field required:
class RegistrationForm(UserCreationForm):
"""Provide a view for creating users with only the requisite fields."""
class Meta:
model = User
# Note that password is taken care of for us by auth's UserCreationForm.
fields = ('username', 'email')
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
This way, you don't have to completely re-define the field, but simply change the property. Hope that helps you out.
The particular case I have is like this:
I have a Transaction model, with fields: from, to (both are ForeignKeys to auth.User model) and amount. In my form, I'd like to present the user 2 fields to fill in: amount and from (to will be automaticly set to current user in a view function).
Default widget to present a ForeignKey is a select-box. But what I want to get there, is limit the choices to the user.peers queryset members only (so people can only register transactions with their peers and don't get flooded with all system users).
I tried to change the ModelForm to something like this:
class AddTransaction(forms.ModelForm):
from = ModelChoiceField(user.peers)
amount = forms.CharField(label = 'How much?')
class Meta:
model = models.Transaction
But it seems I have to pass the queryset of choices for ModelChoiceField right here - where I don't have an access to the web request.user object.
How can I limit the choices in a form to the user-dependent ones?
Use the following method (hopefully it's clear enough):
class BackupForm(ModelForm):
"""Form for adding and editing backups."""
def __init__(self, *args, **kwargs):
systemid = kwargs.pop('systemid')
super(BackupForm, self).__init__(*args, **kwargs)
self.fields['units'] = forms.ModelMultipleChoiceField(
required=False,
queryset=Unit.objects.filter(system__id=systemid),
widget=forms.SelectMultiple(attrs={'title': _("Add unit")}))
class Meta:
model = Backup
exclude = ('system',)
Create forms like this:
form_backup = BackupForm(request.POST,
instance=Backup,
systemid=system.id)
form_backup = BackupForm(initial=form_backup_defaults,
systemid=system.id)
Hope that helps! Let me know if you need me to explain more in depth.
I ran into this problem as well, and this was my solution:
class ChangeEmailForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
self.user = user
super(ChangeEmailForm, self).__init__(*args, **kwargs)
self.fields['email'].initial = user.email
class Meta:
model = User
fields = ('email',)
def save(self, commit=True):
self.user.email = self.cleaned_data['email']
if commit:
self.user.save()
return self.user
Pass the user into the __init__ of the form, and then call super(…). Then set self.fields['from'].queryset to user.peers