Django IntegrityError signup_simplesubscriber.date_created may not be NULL - django

I've read every "InterityError" + "may no be NULL" post and still can't track down what's causing this error.
I've got a two-part signup form. First part is just selecting a product. That passes a product ID to the next page as part of the URL, where they input personal info. I can get the form to work fine until I start removing fields -- i'm using model forms -- because some fields don't need to be displayed.
Here's my model, and the modelForm:
class SimpleSubscriber(models.Model):
name = models.CharField(max_length=255)
address = models.CharField(max_length=200)
city = models.CharField(max_length=100)
state = models.CharField(max_length=2)
zipcode = models.CharField(max_length=9)
phone = models.CharField(max_length=10)
email = models.EmailField()
date_created = models.DateTimeField(null=True)
sub_type = models.ForeignKey(Product)
def __unicode__(self):
return self.name
class SubscriberForm(ModelForm):
class Meta:
model = SimpleSubscriber
fields = ('name', 'address', 'city', 'state', 'zipcode', 'phone', 'email', 'sub_type',)#'date_created',
And here's my views:
def select_product(request):
title = "get yourself an e-edition. wurd."
pform = Product.objects.order_by('product_active')
if request.method == 'POST': # If the form has been submitted...
pform = ProductForm(request.POST) # A form bound to the POST data
if pform.is_valid(): # All validation rules pass
# ...
return HttpResponseRedirect('signup/%i' % pform.id) # Redirect after POST
else:
form = ProductForm() # An unbound form
return render_to_response('signup/index.html', {'title': title, 'pform': pform}, context_instance=RequestContext(request))
def subscriber_signup(request, product_id):
productchoice = Product.objects.get(id=product_id)
now = datetime.datetime.now()
title = "We need some information."
if request.method == 'POST': # If the form has been submitted...
sform = SubscriberForm(request.POST) # A form bound to the POST data
if sform.is_valid(): # All validation rules pass
sform.date_created = now
sform.sub_type = productchoice
sform.save()
return HttpResponseRedirect('thankyou/') # Redirect after POST
else:
sform = SubscriberForm() # An unbound form
return render_to_response('signup/detail.html', {'title': title, 'sform': sform, 'productchoice': productchoice, 'now': now.date(),}, context_instance=RequestContext(request))
I think it has something to do with the modelForm, but I'm pretty new, so I really have no idea. If I add all the fields to SubscriberForm, then they get filled out and everything works fine. But I don't want users to have to say when they filled out the form, so i put sform.date_created = now and I want the product_id to be filled in automatically by what choice they picked on the previous page. but if I exclude these fields from the form it throws the IntegrityError, which isn't very helpful in explaining what to change.
Any hints on where I'm messing up?
Thanks,

Two things:
1) You may benefit from using exlude in your form definition:
class SubscriberForm(ModelForm):
class Meta:
model = SimpleSubscriber
exclude = ('date_created', )
2) To your question, heres how to fix it:
if sform.is_valid(): # All validation rules pass
suscriber = sform.save(commit=False)
suscriber.date_created = now
suscriber.sub_type = productchoice
suscriber.save()

Alternatively to #fceruti's suggestion, you can also add more kwarg tags null=True on the model's field where appropriate - only forcing a minimal set of fields to be completed in the form.

Related

2 Forms on same model not saving as same user - Django

I'm creating a questionnaire / survey, and have two forms (Model Form) built on the same model. These forms are called on separate views, but when saved they appear as separate users in the database. I'm not sure how to get them so save as the same user, I am already using the ' post = form.save(commit=False), post.user = request.user, post.save()' method to save the forms.
EDIT: Added in an attempt to save to the same instance
Model:
class QuizTakers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
industry_choices = (
(1, 'Service'),
(2, 'Hospitality'),
(3, 'Wholesale/Retail'),
(4, 'Manufacturing'),
(5, 'Agriculture')
)
industry = MultiSelectField(choices=industry_choices, max_length=1, max_choices=1)
company_name = models.CharField( max_length=100)
email = models.EmailField(blank=True)
score = models.FloatField(default=0)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.company_name
Forms:
# Form for getting company name
class QuizTakerForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['company_name']
# Form for getting company industry
class QTIndustryForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['industry']
Views:
# view for getting company name
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
request.session['company_name'] = form.cleaned_data['company_name']
post = form.save(commit=False)
post.user = request.user
post.save()
# redirect to a new URL:
return HttpResponseRedirect('industry/')
# if a GET (or any other method) we'll create a blank form
else:
form = QuizTakerForm()
return render(request, 'ImpactCheck/start.html', {'form': form})
# view for getting industry
class IndustryView(FormView):
template_name = 'ImpactCheck/industry.html'
form_class = QTIndustryForm
success_url = '1/'
def get(self, request):
company_name = request.session['company_name']
this_user=QuizTakers.objects.filter(company_name=company_name).order_by('-timestamp').first()
form=self.form_class(instance=this_user)
company_name = request.session['company_name']
return render(request, 'ImpactCheck/industry.html', {'form': form, 'company_name': company_name})
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
post = form.save(commit=False)
post.user = self.request.user
post.save()
return HttpResponseRedirect('/1')
Firstly, in your def start(request) function, you should consider adding the ID to request.session instead of the company name. Something along the lines of
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
form.instance.user=request.user
form.save()
request.session['obj_id'] = post.id
# redirect to a new URL:
return HttpResponseRedirect('industry/')
Now you can use that id to get both the name of your company, as well as the object.
In your IndustryView(FormView), if you're having trouble with the form instances, it's better to use UpdateView instead of the FormView (Be sure to import UpdateView first)
class IndustryView(UpdateView):
template_name = 'ImpactCheck/industry.html'
model = QuizTakers
fields = ['industry']
success_url = '/1'
def get_object(self):
return QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['company_name'] = QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
return ctx
We use the get_context_data method since you need the company_name in your template. The get_object method in this view, tells django which object is to be updated. By default, it grabs the pk from the url (as a url parameter). But since we store our id in the session, we need to explicitly define this function.
Also, since we switched to UpdateView, you no longer need the QTIndustryForm either.

Django ModelForm Custom Date Field

I am trying to create a reservation form that creates an object for model Reservation when the form is posted. I am using a custom datepicker widget to pick the ate, but I am also using ModelForms.
The issue is that, if I do not have 'date' listed in the meta fields list in the forms.py, then the form doesn't look for the date field form input on post. But if I include 'date' inside the meta fields of the forms.py Modelform, then it errors and says "date field can not be left blank" even though it is not blank...
forms.py
class ReservationForm(forms.ModelForm):
date = forms.DateField(
widget=DatePickerInput(format='%m/%d/%Y')
)
def clean_date(self):
data = self.cleaned_data['date']
# Check if a date is not in the past.
if data < datetime.date.today():
raise ValidationError(_('Invalid date - reservation in past'), code='invalid')
messages.danger(request, "Reservation Created")
print('ERROR')
# Remember to always return the cleaned date.
return data
class Meta:
model = Reservation
fields = ('reservation_time', 'people', 'name', 'email', 'phone') # REMOVED 'date'
views.py
def reservationFormView(request):
#reservation = create(Reservation)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = ReservationForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required
reservation = form.save(commit=False)
reservation.ReservationEmail = form.cleaned_data['email']
reservation.ReservationName = form.cleaned_data['name']
reservation.ReservationPeople = form.cleaned_data['people']
reservation.ReservationTime = form.cleaned_data['reservation_time']
reservation.date = form.cleaned_data['date']
print( reservation.date)
#reservation.created_time = timezone.now()
reservation.save()
# redirect to a new URL:
return HttpResponseRedirect('/reservation-confirmation/')
# If this is a GET (or any other method) create the default form.
else:
form = ReservationForm()
return render(request, 'home/reservation_form.html', {'form': form, })
models.py
class Reservation(BaseModel):
class Meta:
verbose_name_plural = "Reservations"
TIME_CHOICES = (
...
)
SEATING_CHOICES = (
...
)
date = models.DateField(null=True)
name = models.CharField(max_length=35, null=True)
phone = PhoneNumberField(null=True) #USE THIS https://github.com/stefanfoulis/django-phonenumber-field
email = models.EmailField(null=True)
people = models.PositiveSmallIntegerField(choices=SEATING_CHOICES, default=None, db_index=True)
reservation_time = models.PositiveSmallIntegerField(choices=TIME_CHOICES, default=None, db_index=True)
def __str__(self):
return '(%s) %s %s' % (self.date, self.name, self.phone )
Your clean_date method does not return a value in case the if condition is False. You should return the cleaned data in case it is correct, like:
def clean_date(self):
data = self.cleaned_data['date']
# Check if a date is not in the past.
if data < datetime.date.today():
raise ValidationError(_('Invalid date - reservation in past'), code='invalid')
messages.danger(request, "Reservation Created")
# not indented under the if
return data
Otherwise, this function will return None in case the data is valid, and raise a ValidationError in case the data is invalid.

Adding a new field to django form

I am totally new in Django and I'm trying to use django forms for the first time. I have searched for this but I still haven't exactly found the answer. Basically I have a view like this:
def pay(request):
if request.method == 'POST':
form = PaymentForm(request.POST)
if form.is_valid():
# I have to calculate the checksum here
myModel = form.save()
else:
print form.errors
else: # The request is GET
form = PaymentForm()
return render_to_response('payment/payment.html', {'form':form})
and I want add an additional field, checksum to the form from the inputs I got from the form So when the user submits the entries the checksum should be added and added to the form and the form should be sent to an external server. But I don't know how to do that (I have defined checksum in my Model). Could anyone help me on this?
My model looks like this:
class PaymentModel(models.Model):
alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$', 'Only alphanumeric characters are allowed!')
secret_key = '6cd118b1432bf22942d93d784cd17084'
pid = models.CharField(primary_key=True, max_length=50, validators=[alphanumeric])
sid = models.CharField(primary_key=True, max_length=50, validators=[alphanumeric])
amount = models.DecimalField(max_digits=9, decimal_places=2)
success_url = 'http://localhost:8000/success'
cancel_url = 'http://localhost:8000/cancel'
error_url = 'http://localhost:8000/error'
checksum = 0
def calc_checksum(self):
checksumstr = "pid=%s&sid=%s&amount=%s&token=%s"% (self.pid, self.sid, self.amount, self.secret_key)
m = md5(checksumstr)
checksum = m.hexdigest()
return checksum
def __unicode__(self): #returns the unicode representation of the object
return self.name
and my form looks like this:
class PaymentForm(ModelForm):
class Meta:
model = PaymentModel
You can use the commit=False keyword argument to form.save():
def pay(request):
if request.method == 'POST':
form = PaymentForm(request.POST)
if form.is_valid():
# Will not save it to the database
myModel = form.save(commit=False)
# keep your business logic out of the view and put it on the form or model
# so it can be reused
myModel.checksum = form.calculate_checksum()
myModel.save()
else:
print form.errors
else: # The request is GET
form = PaymentForm()
return render_to_response('payment/payment.html', {'form':form})
Django form.save() documentation.

A manytomany add with a difficulty because of a form

I am trying to add in a manytomany an object but I have a problem because the user in not the request.user and I can't make a get because the info comes from a form..
My code (in the view):
editedcourse = Course.objects.get(id=Course_id)
# Use the model ChangeCourseOwnerForm.
form = ChangeCourseOwnerForm(instance=editedcourse)
# Test if its a POST request.
if request.method == 'POST':
# Assign to form all fields of the POST request.
form = ChangeCourseOwnerForm(request.POST, instance=editedcourse)
if form.is_valid():
# Save the course.
obj = form.save()
newusername = form['owner'] # Return an User
newusername.userprofile.courses_list.add(editecourse)
The problem is in last two lines because the form doesn't have a "userprofile"... All other code is for comprehension but it works.
The model of the form :
class ChangeCourseOwnerForm(forms.ModelForm):
class Meta:
model = Course
fields = ('owner',)
The model of a Course(I am not english, is it ok at singular the world Course?) :
class Course(models.Model):
name = models.CharField(max_length=30)
description = models.TextField(max_length=30)
owner = models.ForeignKey(User, limit_choices_to={'is_staff': True})
years = models.CharField(max_length=11, choices=YEARS_CHOICES,
default='%d - %d' % (date.year, date.year + 1))
# In Admin panel : object = username
def __unicode__(self):
return self.name
Thanks you for your help, hours of work over that make me foolish :D
It's ok, I have done that :
newowner = form['owner'].value()
new = User.objects.get(id=newowner)
addcourse = new.userprofile.courses_list.add(obj)

Substituting an absent field value when saving a modelform

I have a this model:
class Fleet(models.Model):
company = models.ForeignKey("Company", editable=False)
aircraft = models.ForeignKey("Aircraft")
size = models.IntegerField(default=1)
description = models.TextField(blank=True)
def __unicode__(self):
return u"%s" % (self.aircraft, )
And then a form based on this model:
class FleetForm(ModelForm):
class Meta:
model = Fleet
exclude = ('company', )
When I use this form in a template, the "company" field is not added, which is expected. But that field is required as blank != True.
The way I use this form, the company attribute will always be known in the view function, as it's passed from the URL. How can I add the company to the form in the view function before I save it?
Here is my view:
def new_fleet(request, company_id):
from forms import FleetForm
company = Company.objects.get(pk=company_id)
if request.method == "POST":
form = FleetForm(request.POST,)
form.company = company #doesn't work
form = form.save(commit=False) #can't do this because the form
form.company = company #doesn't validate (company is not set)
if not form.errors:
form.save()
else:
fleet = Fleet(company=company) #pointless because the company widget
form = FleetForm(instance=fleet) #isn't created but eh whatever
There are two ways to solve this issue:
Instantiate your model with initial values for the missing, but required fields:
company = Company.objects.get(pk=company_id)
fleet = Fleet(company=company)
form = FleetForm(request.POST, instance=fleet)
new_fleet = form.save()
Use save(commit=False) and manually set any extra required fields:
company = Company.objects.get(pk=company_id)
form = FleetForm(request.POST)
fleet = form.save(commit=False)
fleet.company = company
new_fleet = fleet.save()
See the note in this section of the ModelForm API documentation for more details.
By the way, either editable=False or exclude is enough to remove a field from a ModelForm; you don't need both.
in #Ayman Hourieh 's answer . Just to address a pitfall in Django. If you have many-to-many field in the form. it would not get saved here. You should explicitly call save_m2m() . add one more line as below.
form.save_m2m()