NOT NULL constraint failed error - django

I want to select an event and upload a photo, but when I make migrations, it gives
NOT NULL constraint failed: myapp_doc.event error.
I'm getting the error even after deleting the view. What should I do ?
models
class Doc(models.Model):
events = (
(None, "choose one of it"),
('bbq', 'Barbeque '),
('meet', 'Meeting'),
)
doc = models.FileField(upload_to='uploads/')
user = models.ForeignKey(User, null=False, blank=True)
event = models.CharField(max_length=15, choices=events, null = True)
def __unicode__(self):
return unicode(self.user)
View
def upload_file(request):
user= request.user
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
doc = form.save(commit=False)
doc.user = request.user
doc.save()
messages.success(request, 'Dosya Yuklendi')
return HttpResponseRedirect('/uploadnew/')
return render(request, 'upload.html', {'form': form})
def upload_file(request):
user= request.user
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
doc = form.save(commit=False)
doc.user = request.user
doc.save()
messages.success(request, 'Dosya Yuklendi')
return HttpResponseRedirect('/uploadnew/')
return render(request, 'upload.html', {'form': form})

Okay I got your problem.
In models.py, you are defining the null = True option for the event column which is of the type models.CharField.
event = models.CharField(max_length=15, choices=events, null = True)
But as a matter of fact, 'CharField' doesn't have the null option in it.
Only the 'max_length' option is available.
See this -> https://docs.djangoproject.com/en/1.9/ref/models/fields/
So technically, you need to remove that null = True part and if you really want to allow the user of your web-app to have the freedom of not selecting any option, then you can add a default value to your event column which will pick a default value from the choices defined by you in the events column.
See the example in the official docs. It's the same as what you want -> https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.Field.choices
So you event column will finally look something like this, if your default value is Barbeque:
event = models.CharField(max_length=15, choices=events, default=bbq)
Also, as shown in the official docs, I don't think you need the single quotes around 'bbq' or 'meet'.
I hope this will solve your problem.

Related

Conditional checks not working while processing form in Django

I have a ModelForm (EditGoalForm) which I use to edit an instance of a model (Goal). Some conditions must be met before saving form data. I used if statements to check these conditions and it still saves, instead of giving an error - like the if statement does nothing.
I have the following:
models.py
class Goal(models.Model):
goal_name = models.CharField(max_length=250)
goal_status = models.ForeignKey(GoalStatus, on_delete=models.CASCADE, related_name='goal_status')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='scrumy_goal_user')
class GoalStatus(models.Model):
status_name = models.CharField(max_length=250)
forms.py
class EditGoalForm(forms.ModelForm):
goal_status = forms.ModelChoiceField(queryset=GoalStatus.objects.all(), empty_label="Select Goal Status")
class Meta:
model = Goal
fields = ('goal_status',)
views.py
def move_goal(request, goal_id):
goal_instance = Goal.objects.get(goal_id=goal_id)
ERROR_MESSAGE = '''BlahBlahBlah'''
has_perm_cannot_move_to_done = request.user.has_perm('application.cannot_move_to_done')
has_perm_can_move_goal_anywhere = request.user.has_perm('application.can_move_goal_anywhere')
has_perm_can_move_someones_goal_from_verify_to_done = request.user.has_perm('application.can_move_someones_goal_from_verify_to_done')
has_perm_can_move_anybodys_goal_to_any_column = request.user.has_perm('application.can_move_anybodys_goal_to_any_column')
if request.method == 'POST':
form = EditGoalForm(request.POST, instance=goal_instance)
if form.is_valid():
if (has_perm_cannot_move_to_done and form.cleaned_data['goal_status'] != 'Done Goal'):
form.save()
messages.success(request, 'Goal Update Successful')
return redirect('home')
else:
messages.error(request, ERROR_MESSAGE)
else:
form = EditGoalForm(instance=goal_instance)
return render(request, 'move_goal.html', {'form': form})
After if form.is_valid, I checked if the authenticated user has the permission and if the goal_status field was not set to Done Goal. If both are True, then save. However, if I set the goal_status field to Done Goal, it still saves instead of displaying an error message. What could be wrong?
form.cleaned_data['goal_status'] is an instance of GoalStatus. It can never be equal to the string 'Goal Done' unless you either:
Implement __eq__ (and/or) __ne__:
def __eq__(self, other):
return self.status_name == other
Just compare what you really want to compare:
form.cleaned_data['goal_status'].status_name != 'Done Goal'

Form with ManyToManyField blank=True and null=True does not save without form.is_valid()

I am saving a form which has a random value generated for one of the field. I also have a manyToMany relation which can be null. What i want to accomplish is, to save the form when it is generated, and later retrieve it to update it.
When i save the form with the admin console, it does let me save without adding anything to it, because for all of my fields for the model have null=True and blank=True.
views.py
def event(request):
if request.POST:
form = EventForm(request.POST)
if form.is_valid():
form.save()
del request.session['event_id']
return HttpResponseRedirect('....')
else:
event_session = request.session.get('event_id')
if event_session is not None:
event_instance = EiEventType.objects.get(eventID = event_session)
form = EiEventForm(instance=event_instance)
form.save(force_update=True)
else:
form = EventForm()
form.save()
request.session['event_id'] = form['eventID'].value()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('.....',args)
I tried with form.is_valid in the else code when i create a new form instance, but it doesn't enter the if condition itself (though not mentioned in the code).
With the current code it returns the error. "EventForm object has no attribute cleaned_data" but saves to the database..When I post(request.POST) with nothing in the form it does save flawlessly, I am not able to understand why is this the case ?
UPDATE:
forms.py
class EventForm(forms.ModelForm):
class Meta:
model = EiEventType
models.py
class Event(models.Model):
eventID = models.CharField(null=True,blank=True,default=random_eventID)
start = models.DateTimeField(null=True, default=two_min_from_now)
signal = models.ManyToManyField(Signal,null=True)
...
...
...
The random function is as defined below,
def random_eventID()
return "event_" + str(uuid.uuid4())[:5]
def two_min_from_now()
return datetime.datetime.now() + timedelta(minutes=2)
You cannot call form.save() unless you first call form.is_valid().
You have a form.save() method call with no data bound to the form:
else:
form = EventForm()
form.save()
request.session['event_id'] = form['eventID'].value()
Hence, as it's a modelform, it looks for the cleaned data, but it doesn't exist.

Set form field value before is_valid()

I'm having a bit of trouble grasping how to do this. I've put my best effort into searching Google without any luck.
I'll start with a bit of code and explain what I'm trying to do as I go:
models.py
class Action(models.Model):
title = models.CharField(max_length=200)
owner = models.ForeignKey(User, related_name='actions')
created_by = models.ForeignKey(User, related_name='+', editable=False)
modified_by = models.ForeignKey(User, related_name='+', editable=False)
class ActionForm(ModelForm):
class Meta:
model = Action
views.py
By default, there is a dropdown field for owner. I have an icon that allows the user to enter a new username in a text field instead for owner. I check to see if owner_new was submitted and if so, create that user. I then need to set the owner field to that value so that form.is_valid() will be true.
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user = User.objects.get(username=request.POST.get('owner_new'))
if not user:
user = User.objects.create_user(request.POST.get('owner_new'))
# HERE IS WHERE I'M STUMPED
form.owner = user.id
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
You can try this:
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
updated_data = request.POST.copy()
updated_data.update({'owner': user})
form = MyForm(data=updated_data)
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
A cleaner way of doing this is:
add required=False to the owner field.
Now,
if form.is_valid(): # THIS DOES NOT FAIL EVEN IF form.owner ISN'T SET
action = form.save(commit=False)
if 'owner_new' in request.POST:
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
action.owner = user
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
I came into a similar situation and couldn't figure out how to do it the way I wanted. What I ended up with was putting a link to a UserForm which allows a user to create a new owner, and then redirect back to the ActionForm with the argument initial={owner: new_owner} included when instantiating the form.

Add a Manytomany item in a list after a request

I need to make an automatic add in a ManyToMany field. My Class :
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='student')
courses_list = models.ManyToManyField(Course, blank=True)
After saving a new Course I want to add it to course_list of the user :
def newcourse(request):
if not request.user.is_authenticated():
return render_to_response('login.html')
form = CourseForm()
if request.method == 'POST':
form = CourseForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.owner = request.user
obj = form.save()
course_list = request.user.userprofile.courses_list.all()
course_list += form
course_list.save()
return render(request, 'mycourses.html')
return render(request, 'newcourse.html', locals())
But it doesn't works : `unsupported operand type(s) for +=: 'ManyRelatedManager' and 'CourseForm'``
Maybe I need to make an new request ?
If you have an idea.. :D
You need to do the following:
request.user.userprofile.courses_list.add(obj)
See the docs on ManyToMany relationships for more detail:
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
Of course, you should probably handle getting the profile in the "proper" way as well:
try:
profile = request.user.get_profile()
profile.courses_list.add(obj)
except UserProfile.DoesNotExist:
messages.error(request, "Couldn't find profile")

Django form is only valid after second request

I have a very strange problem with django forms, I display a form which includes an additional formset so that the user can also submit data for a foreign key relation at the same time.
The template always displays a form for the original model and one form for the second model.
I now want to submit the two forms without filling in anything in the second form.
On the first submission the seond form does not validate and the page is redisplayed, but on the second submission the second form is valid! Even so the POST data is identical.
How can this be possible?
Or maybe I am doing this completely wrong, how can you discern if the user did not fill in anything in the formset or if he filled in something invalid?
Here the models:
class Software(models.Model):
creation_date = models.DateTimeField(default=datetime.now)
creator = models.ForeignKey(User)
version = models.CharField(max_length=300, unique=True, editable=False)
major_version = models.IntegerField()
minor_version = models.IntegerField()
[...]
def save(self, **kwargs):
"""
This updates the version string to the combined representation.
"""
self.version = Software.combine_version_string (self.major_version, self.minor_version)
super(Software, self).save(**kwargs)
class SoftwarePatch(models.Model):
file = models.FileField(upload_to='software_patches')
file_name = models.CharField(max_length=255, editable=False)
file_date = models.DateTimeField(default=datetime.now)
upload_date = models.DateTimeField(default=datetime.now)
software = models.ForeignKey('Software', related_name='patches')
firmware_patch = models.BooleanField(default=True)
target_path = models.CharField(max_length=255, blank=True)
class Meta:
unique_together = ('software', 'file_name')
verbose_name_plural = "software patches"
def __unicode__(self):
return self.file_name
def clean(self):
if self.file and not self.file_name:
self.file_name = self.file.file.name
Here my forms:
SoftwarePatchFormSet = inlineformset_factory(Software,
SoftwarePatch,
extra=1)
class SoftwareForm(forms.ModelForm):
"""
A simple form for creating a new software.
"""
class Meta:
model = Software
And finally my view function:
def software_add(request, software_id=None):
if software_id == None:
software = Software()
else:
software = Software.objects.get(id=software_id)
if request.POST:
form = SoftwareForm(request.POST, instance=software)
if form.is_valid():
software = form.save(commit=False)
softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES, instance=software)
if softwarepatch_formset.is_valid():
software = form.save()
softwarepatch_formset.save()
# Redirect, in case of a popup close it
if request.POST.has_key("_popup"):
pk_value = software._get_pk_val()
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
(escape(pk_value), escape(software)))
if 'next' in request.POST:
return HttpResponseRedirect(request.POST['next'])
else:
return HttpResponseRedirect(reverse('index'))
else:
form = SoftwareForm(instance=software)
softwarepatch_formset = SoftwarePatchFormSet(instance=software)
is_popup = request.GET.has_key("_popup") or request.POST.has_key("_popup")
return render_to_response(
'main/software_edit.html',
{'form': form,
'softwarepatch_formset': softwarepatch_formset,
'add': True,
'is_popup': is_popup,
},
context_instance = RequestContext(request)
)
First of all, you should set the instance argument only when creating a form / formset for an existing object i.e. one already in the DB. So for example if software_id = None and it's a GET request, you should only do form = SoftwareForm().
Also, after doing software = form.save(commit=False), you should do software.save() instead of software = form.save(). [I don't think it's really a problem though, just that you're redoing a save]. Remember that if you have a ManyToManyField in the Software model, you need to do form.save_m2m() after software = form.save() as well.
Here's what I think you should have:
def software_add(request, software_id=None):
if request.POST:
if software_id:
software = Software.objects.get(id=software_id)
form = SoftwareForm(request.POST, instance=software)
else:
form = SoftwareForm(request.POST)
if form.is_valid():
software = form.save(commit=False)
softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES, instance=software)
if softwarepatch_formset.is_valid():
software.save()
softwarepatch_formset.save()
# Redirect, in case of a popup close it
if request.POST.has_key("_popup"):
pk_value = software._get_pk_val()
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
(escape(pk_value), escape(software)))
if 'next' in request.POST:
return HttpResponseRedirect(request.POST['next'])
else:
return HttpResponseRedirect(reverse('index'))
else:
softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES)
else:
if software_id:
software = Software.objects.get(id=software_id)
form = SoftwareForm(instance=software)
softwarepatch_formset = SoftwarePatchFormSet(instance=software)
else:
form = SoftwareForm()
softwarepatch_formset = SoftwarePatchFormSet()
is_popup = request.GET.has_key("_popup") or request.POST.has_key("_popup")
return render_to_response(
'main/software_edit.html',
{'form': form,
'softwarepatch_formset': softwarepatch_formset,
'add': True,
'is_popup': is_popup,
},
context_instance = RequestContext(request)
)
Ok I finally found my problem!
I have the following model field: file_date = models.DateTimeField(default=datetime.now)
This sets the innital-file-date to a value like this: u'2011-10-18 08:14:30.242000'
After being rendered through the html widget the value will be: u'2011-10-18 08:14:30'
So django will think the form was changed and therefore not save.
On the second load django will automatically set the truncated value as initial-file-date and then nothing is changed and the save works as expected.
So now I only have to figure out what to use instead of datetime.now. I will update this post when I have figured it out.