model
class Person(models.Model):
first_name = models.CharField(blank=False,max_length=256,default='')
last_name = models.CharField(blank=False,max_length=256,default='')
plan = models.CharField(blank=False,max_length=256,default='')
plan_price = models.CharField(blank=False,max_length=256,default='')
Views.py
if request.method == 'POST':
if form.is_valid():
form.save(commit=True)
return index(request)
In my modelForm I accept 3 values from the user: first_name, last_name, and plan. I dont have any problem with posting to the database from the form, what i am trying to find out is how I can say something like this
if plan = 'plan1':
#set plan_price to '$399'
else
#set plan_price to '$699'
#then post first_name, last_name, plan, plan_price to database
You can try the following:
if form.is_valid():
person = form.save(commit=False)
plans = {
'plan1': 399,
'plan2': 699,
# ...
}
person.plan_price = plans.get(person.plan, some_default)
person.save()
return index(request)
# you might consider a redirect instead so as not to have the same content on various urls
If the plan price always should match the plan you can also override the model's save method and leave the view as you had it:
class Person(models.Model):
# ...
def save(self, *args, **kwargs):
self.plan_price = some_logic(self.plan)
super(Person, self).save(*args, **kwargs)
But then, you could replace that field by a property alltogether as it seems redundant. If plans, for instance, change prices, I would consider a Plan model with a name and price field.
in your createview you can use this function and write your code there
def form_valid(self, form):
if self.object.plan = 'plan1':
form.instance.price = 399
else:
[...]
return super(your_class_name, self).form_valid(form)
you can access the created object fields by self.object.filed
Related
I'm new to Django and I have built a Form that shows a single select field to chose from. The data in the field are calculated on the go by the Form.
I now need, once the data is being submitted, to be save on the database. The only problem is that, for some reason, I got an IntegrityError error NOT NULL constraint failed: manager_playlist.user_id
Below my view, form and model in Django
views.py
def playlist(request):
if not is_user_already_auth_spotify(request):
messages.error(request, "You're not authenticated with Spotify, please authenticate here")
return redirect('/members/account/' + request.user.username)
if request.method == "POST":
form = ChoosePlaylistForm(request.POST, request=request)
if form.is_valid():
form.save()
messages.success(request, "Playlist successfully chosen")
return HttpResponseRedirect('account')
else:
pass
else:
form = ChoosePlaylistForm(request=request)
return render(request, 'show_playlist.html', {"playlist_choose_form": form})
forms.py
class ChoosePlaylistForm(ModelForm):
playlists = forms.ChoiceField(choices=())
class Meta:
model = Playlist
fields = ('playlists',)
def __init__(self, *args, request=None, **kwargs):
super(ChoosePlaylistForm, self).__init__(*args, **kwargs)
self.request = request
self.fields['playlists'].choices = self.generate_selection()
def generate_selection(self):
sp_auth, cache_handler = spotify_oauth2(self.request)
spotify = spotipy.Spotify(oauth_manager=sp_auth)
s_user = spotify.current_user()
u_playlists = spotify.user_playlists(s_user['id'], limit=10)
choices = []
for playlist in u_playlists["items"]:
if playlist["owner"]["id"] == s_user['id']:
playlist_choice = (playlist["id"], playlist["name"])
choices.append(playlist_choice)
else:
pass
return choices
model.py
class Playlist(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
playlists = models.CharField(max_length=50, null=True, blank=True) # playlists are the ids of the playlists
def __str__(self):
return self.playlists
The reason for the error is that when a new Playlist object is created, the user field must not be empty (you did not add null=True, and of course, would not make sense here if you did). Now the form validates because the form does not require the user field, only the playlists field. You have a couple of choices.
Option 1
Add the required field to your form (I haven't tested this, please check the docs!):
class ChoosePlaylistForm(ModelForm):
playlists = forms.ChoiceField(choices=())
class Meta:
model = Playlist
fields = ('playlists', 'user',) # NOTE THE CHANGE HERE
def __init__(self, *args, request=None, **kwargs):
super(ChoosePlaylistForm, self).__init__(*args, **kwargs)
self.request = request
self.user = request.user # Add the user to the form
Option 2
Save the form as is using commit=False, then add the missing field before you save your model:
if request.method == "POST":
form = ChoosePlaylistForm(request.POST, request=request)
if form.is_valid():
playlist = form.save(commit=False) # NOTE THE CHANGE HERE
playlist.user = request.user # Add the user to the partial playlist
playlist.save() # Now you can save the playlist
messages.success(request, "Playlist successfully chosen")
return HttpResponseRedirect('account')
Option 3
Add the field when you instantiate the form itself (I'm not sure my syntax is correct here):
form = ChoosePlaylistForm(request.POST, request=request, instance=request.user)
EDIT
Option 3 above does not seem to work. I believe this edit will:
form = ChoosePlaylistForm(request.POST, request=request, initial={'user': request.user})
I am currently working on our user profile. A user can add multiple E-Mail addresses to his/her account.
views.py
#login_required(login_url='/login')
def profile_update_emails(request):
context = {}
...
try:
email = CustomerEmails.objects.get(customer=request.user)
update_emails_form = UpdateEmailsForm(request.POST or None, instance=email)
except CustomerEmails.DoesNotExist:
update_emails_form = UpdateEmailsForm(request.POST or None)
context.update({'update_emails_form': update_emails_form})
if request.POST:
if update_emails_form.is_valid():
update_emails_form.save(commit=False)
update_emails_form.customer = request.user
update_emails_form.save()
messages.success(request, "All good")
return render(request, 'usercp/profile.html', context)
forms.py
class UpdateEmailsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(forms.ModelForm, self).__init__(*args, **kwargs)
for f in self.fields:
self.fields[f].widget.attrs['class'] = 'form-control'
class Meta:
model = CustomerEmails
fields = ('email',)
models.py
class CustomerEmails(models.Model):
customer = models.ForeignKey(Customer)
email = models.CharField(max_length=255, unique=True)
error/trace:
IntegrityError at /usercp/profile/profile_update_emails
(1048, "Column 'customer_id' cannot be null")
Please note I have a custom user model. Which is not the problem here.
I am not quite sure, why the customer field is not getting populated before the second save() though. There are currently no rows in that table for the CustomerEmails model. (I know that this will clash with the .get() in the future, since a user can have multiple e-mails, but first things first)
You need to set the relationship on the model instance, which is returned from the form save, not on the form itself.
obj = update_emails_form.save(commit=False)
obj.customer = request.user
obj.save()
I have created a model Student which extends from the Django User and is a foreign key to another model while it has an integer field called year. What i'm trying to do is to save a form, which has 2 fields. The one is the course id and the another one is the the integer field year. When I'm clicking submit, i'm getting an error Cannot assign "u'2'": "Student.course" must be a "Course" instance.
models.py
class Student(models.Model):
user = models.OneToOneField(User)
course = models.ForeignKey(Course)
year = models.IntegerField(validators=[MinValueValidator(1),
MaxValueValidator(7)])
view.py
def step3(request):
user = request.user
if request.method == 'POST':
form = SelectCourseYear(request.POST)
if form.is_valid():
form.save()
return render_to_response("registration/complete.html", RequestContext(request))
else:
form = SelectCourseYear()
return render(request, 'registration/step3.html',)
forms.py
class SelectCourseYear(forms.ModelForm):
course = forms.CharField()
year = forms.IntegerField(required=True)
class Meta:
model = Student
fields = ['user', 'course', 'year']
You dont need to redefine fields in the ModelForm if you've already mentioned them in the fields attribute. So your form should look like this -
class SelectCourseYear(forms.ModelForm):
class Meta:
model = Student
fields = ['course', 'year'] # removing user. we'll handle that in view
And we can handle the form with ease in the view -
def step3(request):
user = request.user
if request.method == 'POST':
form = SelectCourseYear(request.POST)
if form.is_valid():
student = form.save(commit=False)
# commit=False tells Django that "Don't send this to database yet.
# I have more things I want to do with it."
student.user = request.user # Set the user object here
student.save() # Now you can send it to DB
return render_to_response("registration/complete.html", RequestContext(request))
else:
form = SelectCourseYear()
return render(request, 'registration/step3.html',)
course has to be an instance of a Course model, not just the primary key of the instance. You can still accept an id in the form as a text input, but you're going to need to retrieve the actual course instance and assign the value.
You'll need to verify that the course id is valid, so putting that code into the clean method isn't a bad idea. Notice also how the course field is excluded here? Otherwise the form will expect it to be present. You also don't need to re-define the year field, as the ModelForm will inherit that field from the Student model.
# forms.py
class SelectCourseYear(forms.ModelForm):
class Meta:
model = Student
exclude = ['user', 'course']
course_id = forms.IntegerField()
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(SelectCourseYear, self).__init__(*args, **kwargs)
def clean_course_id(self):
course_id = self.cleaned_data.get('course_id')
try:
self.course = Course.objects.get(pk=course_id)
except Course.DoesNotExist:
raise forms.ValidationError('Sorry, that course id is not valid.')
return course_id
def save(self, commit=True):
instance = super(SelectCourseYear, self).save(commit=False)
instance.course = self.course
instance.user = self.user
if commit:
instance.save()
return instance
# views.py
def step3(request):
if request.method == 'POST':
form = SelectCourseYear(request.POST or None, user=request.user)
if form.is_valid():
form.save()
return render_to_response("registration/complete.html",
RequestContext(request))
return render(request, 'registration/step3.html',)
Now, when you call .save() on the model, the course field will be assigned an instance of Course
I'm having a problem with logged users and a Django ModelForm. I have a class named _Animal_ that has a ForeignKey to User and some data related to the animal like age, race, and so on.
A user can add Animals to the db and I have to track the author of each animal, so I need to add the request.user that is logged when the user creates an animal instance.
models.py
class Animal(models.Model):
name = models.CharField(max_length=300)
age = models.PositiveSmallIntegerField()
race = models.ForeignKey(Race)
...
publisher = models.ForeignKey(User)
def __unicode__(self):
return self.name
class AnimalForm(ModelForm):
class Meta:
model = Animal
The main goal is hide the publisher field in the form, and submit the logged user when hitting save button.
I can catch the current user in the view using initial, but what I also want is not display the field.
views.py
#login_required
def new_animal(request):
if request.method == "POST":
form = AnimalForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
variables = RequestContext(request, {'form': form})
return render_to_response('web/animal_form.html', variables)
else:
form = AnimalForm(initial={'publisher': request.user})
variables = RequestContext(request, {'form': form})
return render_to_response('web/animal_form.html', variables)
You just need to exclude it from the form, then set it in the view.
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
... and in the view:
form = AnimalForm(request.POST)
if form.is_valid():
animal = form.save(commit=False)
animal.publisher = request.user
animal.save()
(Note also that the first else clause - the lines immediately following the redirect - is unnecessary. If you leave it out, execution will fall through to the two lines at the end of the view, which are identical.)
Another way (slightly shorter):
You need to exclude the field as well:
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
then in the view:
animal = Animal(publisher=request.user)
form = AnimalForm(request.POST, instance=animal)
if form.is_valid():
animal.save()
I would add it directly to the form:
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
def save(self, commit=True):
self.instance.publisher = self.request.user
return super().save(commit=commit)
This is in my opinion the cleanest version and you may use the form in different views.
If you are using ModelAdmin
you should add method get form on your ModelAdmin
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, **kwargs)
form.request = request
return from
and you can now access request in your ModelForm
class ProductAdminForm(forms.ModelForm):
def save(self, commit: bool, *args, **kwargs):
self.instance.user = self.request.user
return super().save(commit=commit)
pass
I know this seems to be an over-asked question in the Django circles but I'm afraid to say that i've not yet found the solution to this.
My Model -
from djago.... import User
class InfoPersonal(models.Model):
...
person = models.OneToOneField(User)
I've tried overriding the save_model() in the admin definition and also overriding the save() in the Form but nothing seems to work.
If you were to auto add data into a ForeignKey or OneToOneField column to a Model how would you do it?
def profile_creation_personal(request):
if request.method == 'POST': # If the form has been submitted...
form = PersonalForm(request.POST) # A form bound to the POST data
# form.person = request.user
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
form.save()
return HttpResponseRedirect('/done') # Redirect after POST
else:
form = PersonalForm() # An unbound form
return render_to_response('info/personal/profile_create.html', { 'form': form,})
class PersonalForm(ModelForm):
#hometown_id = ModelChoiceField(queryset=InfoReferenceCities.objects.all(),empty_label=None)
def save(self, *args, **kwargs):
self.person = request.user
super(PersonalForm, self).save(*args, **kwargs)
class Meta:
model = InfoPersonal
exclude = ('person',)
widgets = {'dateofbirth' : SelectDateWidget()}
I got the answer!!! I feel good!
personal = form.save(commit = False)
personal.person = request.user
personal.save()
This goes into the view much like Ignacio said, only commit = False being a critical statement for it to save an instance without throwing an exception. Thanks all who helped!!
Cheers
In your PersonalForm, you can subclass your save() function, so the appropriate data is added, something like this:
class PersonalForm(ModelForm):
def save(self, *args, **kwargs):
self.person = request.user
super(PersonalForm, self).save(*args, **kwargs)
class Meta:
model = Personal
see this
parent = models.ForeignKey('self', blank=True, null=True, verbose_name=_("parent"))
this ok but have problem with sqlite , change it to postgresql its ok. ( its for my code , change it to your status )