I have a model with 'created' field. I want store date only when new object is created, but do not update this field when I change some other fields in model. Before sync i had auto_now but I changed it like in first answer Automatic creation date for django model form objects?. My problem is when I update my entity
IntegrityError at /rhyme/15/
null value in column "created" violates not-null constraint
DETAIL: Failing row contains (15, LORE, <p>LORE</p>
<p>LORE</p>
<p>LORE</p>
<p>LORE</p>
<p>LORE</p>
<p>L..., null, 1, 1).
My model:
class Rhyme(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(UserProfile, related_name='created_rhymes')
profiles = models.ManyToManyField(UserProfile, related_name='stored_rhymes')
category = models.ForeignKey(Category, null=True, blank=True, related_name='rhymes')
update
view:
form = RhymeForm(data)
form_is_valid = form.is_valid()
if form_is_valid:
rhyme = form.save(commit=False)
if self.kwargs.has_key('id'):
rhyme.id = self.kwargs['id']
rhyme.author = self.request.user.profile
rhyme.save()
form:
class RhymeForm(forms.ModelForm):
title = forms.CharField(
widget=forms.TextInput(attrs={'class':'form-control'}),
label=u'Tytuł'
)
content = forms.CharField(
widget=forms.Textarea(attrs={'class':'form-control'}),
label=u'Treść'
)
category = forms.ModelChoiceField(
queryset=Category.objects.all(),
label=u'Kategoria',
empty_label=" ",
required=False
)
class Meta:
model = Rhyme
fields = ('title', 'content', 'category')
def save(self, commit=True):
rhyme = super(RhymeForm, self).save(commit=False)
if commit is True:
rhyme.save()
return rhyme
template (bit weird but json sends just category, content and title):
<form action="{$ formAction $}" role="form" method="post" ng-submit="add($event)">
{% for field in form.visible_fields %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
{{ field.label_tag }}
{{ field|ngmodel }}
{% with 'errors.'|add:field.name as error_field %}
<div class="text-warning bg bg-danger">
{{ field.errors }}{$ {{ error_field }} $}
</div>
{% endwith %}
</div>
{% endfor %}
{% csrf_token %}
<div class="row">
<button ng-click="close($event)" class="btn btn-default col-md-3">Anuluj</button>
<input class="btn btn-success col-md-offset-1 col-md-8" type="submit" value="Zapisz" />
</div>
</form>
My ugly solution is to put
rhyme.created = models.Rhyme.objects.get(pk=self.kwargs['id']).created
But why my ORM trying to put null to field created, how to solve this?
Related
I need to update and, if needed, create elements in a Django update view. Basically, I have a form where I am giving the user the chance of updating a row or inserting one or more new rows.
The problem is that I am having issues in updating the "old" rows. If I update an existing row, it creates a new one.
Here I post some code:
views.py
def edit_flight_mission(request, pk):
mission = Mission.objects.get(id=pk)
form = EditMissionForm(request.POST or None, instance=mission)
learning_objectives = LearningObjective.objects.filter(mission_id=mission)
context = {
'mission': mission,
'form': form,
'learning_objectives': learning_objectives,
}
if request.method == 'POST':
learning_obj = request.POST.getlist('learning_obj')
solo_flight = request.POST.get('solo_flight')
if form.is_valid():
mission_obj = form.save()
if solo_flight == 'solo_flight':
mission_obj.solo_flight = True
mission_obj.save()
for lo in learning_obj:
learning_objective, created = LearningObjective.objects.get_or_create(name=lo, mission_id=mission.id)
if not created:
learning_objective.name = lo
learning_objective.save()
return render(request, 'user/edit_flight_mission.html', context)
models.py
class Mission(models.Model):
name = models.CharField(max_length=200)
duration_dual = models.DurationField(blank=True, null=True)
duration_solo = models.DurationField(blank=True, null=True)
training_course = models.ForeignKey(
TrainingCourse, on_delete=models.CASCADE)
note = models.TextField(null=True, blank=True)
solo_flight = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class LearningObjective(models.Model):
name = models.CharField(max_length=300)
mission = models.ForeignKey(Mission, on_delete=models.CASCADE, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
forms.py
class EditMissionForm(forms.ModelForm):
class Meta:
model = Mission
fields = ('name', 'duration_dual', 'duration_solo', 'training_course')
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter Mission Name'}),
'duration_dual': forms.TextInput(attrs={'class':'form-control', 'placeholder': 'Duration as HH:MM:SS'}),
'duration_solo': forms.TextInput(attrs={'class':'form-control', 'placeholder': 'Duration as HH:MM:SS'}),
'training_course': forms.Select(attrs={'class': 'form-control'}),
}
template
{% extends "base.html" %}
{% block head_title %}
Edit Flight Mission {{mission.id}}
{% endblock head_title %}
{% block title %}
Edit Flight Mission {{mission.id}}
{% endblock title%}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="card-body">
<div class="form-group">
{{form.as_p}}
</div>
<div class="form-group">
<div id="inputFormRow">
<label>Learning Objective</label>
{% for lo in learning_objectives %}
<div class="input-group mb-3">
<input type="text" value="{{lo.name}}" class="form-control" name="learning_obj" placeholder="Learning Objective">
<div class="input-group-append">
</div>
</div>
{% endfor %}
<div id="newRow"></div>
<div class="form group">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="solo_flight" value="solo_flight" id="flexCheckDefault">
<label class="form-check-label" for="flexCheckDefault">
Solo Flight
</label>
</div>
</div>
<button id="addRow" type="button" class="btn btn-primary mb-3">Add Learning Objective</button>
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary btn-block">
Add New Mission
</button>
</div>
</form>
{% endblock content %}
{% block custom_js %}
<script type="text/javascript">
// add row
$("#addRow").click(function () {
var html = '';
html += '<div id="inputFormRow">';
html += '<div class="input-group mb-3">'
html += '<input type="text" class="form-control" name="learning_obj" placeholder="Learning Objective">'
html += '<div class="input-group-append">'
html += '<button class="btn btn-danger" type="button" id="remove">Remove</button>'
html += '</div></div>'
$('#newRow').append(html);
});
// remove row
$(document).on('click', '#remove', function () {
$(this).closest('#inputFormRow').remove();
});
</script>
{% endblock custom_js %}
The form is updating correctly but the problem is with the part concerning the Learning Objectives basically.
Any suggestion?
The problem is here:
learning_objective, created = LearningObjective.objects.get_or_create(name=lo, mission_id=mission.id)
Specifically, the mission_id=mission.id part. If you want to do a lookup to a ForeignKey, you need two underscores. Therefore, the query is not finding the LearningObjective, thus it is always creating a new one. But it's not even necessary, since you've already filtered learning_objectives by mission (and there, it was done with the correct syntax).
The solution, then is to do this:
learning_objective, created = LearningObjective.objects.get_or_create(name=lo)
if not created:
learning_objective.name = lo
learning_objective.save()
The solution, though can be done much easier with update_or_create. This is the same as what you're doing, but in one line instead of 4.
learning_objective, created = LearningObjective.objects.update_or_create(name=lo, defaults={'name': lo})
Edit
I think the syntax here is actually not correct. Change it as follows:
# Change this,
# learning_objectives = LearningObjective.objects.filter(mission_id=mission)
# To this:
learning_objectives = LearningObjective.objects.filter(mission=mission)
Edit 2
I'm not sure if this problem is what's causing the learning_objectives not to save, but I now see another error in the html. You can not have a form within another form. The {{ form.as_p }} is creating another <form> tag within the one you already have. So the form is validating because all the fields of the {{ form.as_p }} are there, but those are for the Mission object. Are the other fields even being submitted? Check by print(request.POST). I'm guessing that it will not contain the name field for the learning_obj.
Possible Solutions:
Create a form, but not a ModelForm, that has everything you want to save from the two different models.
Render the {{ form }} manually, so that the <form> tags are not there. When you submit, all inputs with names will be submitted.
I have a django project where I have a list with checkboxes, to select and assign students to teachers.
I have attached my code below - I am getting no errors, but it doesn't seem to be assigning the student to the "students" many-to-many field in my "teachers" model.
I have a GET function that is called earlier to select the teacher, then it is meant to pull that GET record and assign the student to that teacher.
Any ideas?
Views.py
if 'assign_student' in request.POST:
form = StudentForm(request.POST)
selected = request.GET['select_teacher']
selected_teacher = Teacher.objects.all().filter(pk=selected)[0]
if form.is_valid():
student_selected = request.POST('assign_student')
student = Student.objects.all().filter(pk=student_selected)[0]
selected_teacher.students.add(student)
Template.html
<div class="student-list">
{% for instance in all_students %}
<div class="input-group" style="border-bottom: 1px solid;">
<div class="item-details">
<div class="form-check">
<form method="POST" class="form-group" name="assign_student">
{% csrf_token %}
<input onChange="this.form.submit()" class="form-check-input" type="checkbox" value={{ instance.pk }} id="flexCheckDefault" name="assign_student">
</form>
<p>
<b>{{ instance.fullname }}</b>
<br>
{{ instance.homeschool }} - {{ instance.schoolclass }} Class
</p>
</div>
</div>
</div>
{% endfor %}
</div>
Models.py
class Teacher(models.Model):
fullname = models.CharField(max_length=10000,verbose_name='Full Name')
firstname = models.CharField(max_length=10000,verbose_name='First Name')
lastname = models.CharField(max_length=10000,verbose_name='Last Name')
school = ForeignKey(School,on_delete=models.CASCADE,verbose_name='School')
creationuser = ForeignKey(CustomUser,on_delete=models.CASCADE)
students = models.ManyToManyField(Student)
class Role(models.TextChoices):
principal = 'principal'
classroomteacher = 'classroom teacher'
assistantprincipal = 'assistant principal'
role = models.CharField(
max_length=10000,
choices=Role.choices,
verbose_name='Role',
)
email = models.EmailField(verbose_name='Email Address')
def __str__(self):
return self.fullname
The reason it is not working is because you are using queries by using filters etc. Thus you are returned a query set instead of an object.
Instead you should use the get_object_or_404().
Furthermore, you are saving the data for the session and not in the database. You should use students.save() to save it in the DB.
You can do something like this
from django.shortcuts import get_object_or_404
if 'assign_student' in request.POST:
form = StudentForm(request.POST)
selected = request.GET['select_teacher']
selected_teacher = get_object_or_404(Teacher, pk=selected)
if form.is_valid():
student_selected = request.POST('assign_student')
student = get_object_or_404(Student, pk=student_selected)
selected_teacher.students.add(student)
selected_teacher.save() #saving the teacher model object
selected_teacher.students.save() #saving the student model object
Ah im silly!
I fixed it, I was trying to get a "if form.is_valid()" when i'm not posting a full form - removed that and all is working now.
I'm trying to display the employee data after filtering the DB by pk which I passed via the URL.
I can update the form, although I don't want the form fields to be empty since I just want to update, which means its not all the fields I'm going to touch
forms.py
class AddEmployeeForm(forms.Form):
genderset = [(0,'--Select an Option--'),('Male','Male'), ('Female', 'Female')]
marital_status_set = [(0,'--Select an Option--'),('Married','Married'), ('Single', 'Single')]
employment_type_set = [(0,'--Select an Option--'),('Contract','Contract'), ('Full-Time', 'Full-Time'),('Intern', 'Intern')]
employment_status_set = [(0,'--Select an Option--'),('Active','Active'), ('Inactive', 'Inactive')]
first_name = forms.CharField(label = "First Name ", max_length = 200)
last_name = forms.CharField(label = "Last Name ", max_length = 200)
employee_id = forms.IntegerField()
email = forms.EmailField(label = "Email ", max_length = 200)
address = forms.CharField(label = "Address", max_length = 200)
role = forms.CharField( max_length = 200)
date_of_birth = forms.DateField()
join_date = forms.DateField()
end_date = forms.DateField()
location = forms.CharField( max_length = 200)
hod = forms.ModelChoiceField(queryset=Department.objects.only('lead'))
phone_number = forms.CharField( max_length = 200)
employment_type = forms.ChoiceField( choices = employment_type_set)
employment_status = forms.ChoiceField( choices = employment_status_set )
marital_status = forms.ChoiceField( choices = marital_status_set )
gender = forms.ChoiceField( choices = genderset )
department = forms.ModelChoiceField(queryset=Department.objects.only('dept_name'))
credentials = forms.FileField()
passport = forms.FileField()
date_added = forms.DateTimeField( initial = datetime.now, widget=forms.HiddenInput())
views.py
#login_required(login_url='/accounts/login')
def edit(request, pk):
employee = Employee.objects.filter(pk=pk)
form = AddEmployeeForm()
context = {
'employee': employee,
'form':form
}
return render(request, 'employees/edit.html', context)
edit.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="page-wrapper">
<div class="content container-fluid">
<div class="row">
<div class="col-xs-4">
<h4 class="page-title">Edit Employee Details</h4>
</div>
<div class="col-xs-4 text-center">
{% include "partials/_alerts.html" %}
</div>
</div>
<form class="m-b-30" action="{% url 'add' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row"></div>
{% for field in form %}
<div class="col-sm-6">
<div class="form-group">
{{ field.errors }}
{{ field|as_crispy_field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<div class="m-t-20 text-center">
<button class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
{% endblock content %}
Its meant to display the values of an employee that was filterd from the database using the PK value
In views.py, you can pass a dictionary into AddEmployeeForm constructor to display the values:
#login_required(login_url='/accounts/login')
def edit(request, pk):
employee = Employee.objects.filter(pk=pk)
field_values = { 'first_name': employee.first_name } #...other fields
form = AddEmployeeForm(field_values)
context = {
'employee': employee,
'form':form
}
return render(request, 'employees/edit.html', context)
A Form instance is either bound to a set of data, or unbound.
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the
HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
Reference https://docs.djangoproject.com/en/2.2/ref/forms/api/#ref-forms-api-bound-unbound
You can create a form instance and use this format:
form = In_Form(initial={'username': name})
And in your template you can then populate your form using :
{{employee.name}}
ValueError at /students/addgrregister/
i am trying to add students in gr_register but its giving an error due to this error the code is not working i also upload the template (addgrregister.html) kndly tell me where is the issue in these pages
models.py
class gr_register(models.Model):
Gender_Choices = (
('M', 'Male'),
('FM', 'Female'),
)
Status_Choices = (
('P', 'Present'),
('FM', 'Left'),
)
gr_no = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
date_birth = models.DateField(null=True)
classes_A = models.ForeignKey(Classes, on_delete=models.CASCADE, related_name="classes_A", default=1, verbose_name="Class of Admission")
sections_A = models.ForeignKey(Sections, on_delete=models.CASCADE, related_name="sections_A", default=1, verbose_name="Section of Admission")
gender = models.CharField(max_length=10, choices=Gender_Choices)
classes_C = models.ForeignKey(Classes, on_delete=models.CASCADE, related_name="classes_C", verbose_name="Current Class")
sections_C = models.ForeignKey(Sections, on_delete=models.CASCADE, related_name="sections_C", verbose_name="Current Section")
address = models.CharField(max_length=100, null=True, verbose_name="Home Address")
area_code = models.ForeignKey(Area, on_delete=models.CASCADE, verbose_name="Area")
status = models.CharField(max_length=10, choices=Status_Choices, default='P')
class Meta:
ordering = ('gr_no',)
def __str__(self):
return self.first_name
views.py
from django.shortcuts import get_object_or_404, render, redirect
def addgrregister(request):
if request.method == 'POST':
form = gr_registerForm(request.POST)
if form.is_valid():
form.save()
return redirect('home')
else:
form = gr_registerForm()
return render(request, 'students/addgrregister.html', {'form': form})
forms.py
from django import forms
from django.forms import ModelChoiceField, ModelForm
from .models import *
class gr_registerForm(ModelForm):
classes_A = forms.ModelChoiceField(queryset=Classes.objects.all())
sections_A = forms.ModelChoiceField(queryset=Sections.objects.all())
classes_C = forms.ModelChoiceField(queryset=Classes.objects.all())
sections_C = forms.ModelChoiceField(queryset=Sections.objects.all())
area_code = forms.ModelChoiceField(queryset=Area.objects.all())
class Meta:
model = gr_register
fields = '__all__'
def init(self, *args, **kwargs):
forms.ModelForm.init(self, *args, **kwargs)
addgrregister.html
{% extends 'authenticate/base.html' %}
{% block content %}
<div class="container">
<h4 class="text-center">ADD GR_REGISTER</h4>
<hr/>
<form method="POST" action="{% url 'addgrregister' %}" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-group row">
<label for="id_{{ field.name }}" class="col-2 col-form-label">{{ field.label }}</label>
<div class="col-10">
{{ field }}
</div>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary" name="button">Add GR_REGISTER</button>
</form>
<br/><br/>
</div>
{% endblock %}
There is nothing returned when form is not valid. I think you try like this:
def addgrregister(request):
form = gr_registerForm(request.POST or None) # it initates a form. If the request type is POST, then there will be a dict available with posted data in request.POST. If request is not POST, then the form will initiate with empty data.
if request.method == 'POST':
if form.is_valid(): # Form valid checks if the submitted form is valid or not. If not, it will store errors in the form. When that form is passed to template, it will show errors in html
form.save() # It will store data in DB
return redirect('home')
# when for is invalid, it will show the error in the form
return render(request, 'students/addgrregister.html', {'form': form})
Update
Show form errors in template:
{% for field in form %}
<div class="form-group row">
<label for="id_{{ field.name }}" class="col-2 col-form-label">{{ field.label }}</label>
<div class="col-10">
{{ field }}
{{ field.errors }} // <-- Updated here
</div>
</div>
{% endfor %}
I have been working thorough the DjangoGirls tutorial and was trying to improve on the section on adding comments to an application - TutorialExtensions
I have added the comments to a simple photo blog application but what I was attempting to do was replace the author = models.CharField(max_length=200) with an alternative that would store the current/logged-in user who was commenting on the photo instance and then allow me to display on the photo_detail template.
I thought I was close using author = models.ForeignKey(User, related_name='Commenter') but this through up an error:
NOT NULL constraint failed: timeline_comment.author_id
Here is my models.py consisiting of a Photo model and Comments model:
class Photo(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
title = models.CharField(max_length=120)
slug = models.SlugField(unique=True)
image = ProcessedImageField(upload_to=upload_location,
null=True,
blank=False,
processors=[Transpose(), ResizeToFit(1000, 1000, False)],
format='JPEG',
options={'quality': 50},
width_field="width_field",
height_field="height_field")
height_field = models.IntegerField(default=0)
width_field = models.IntegerField(default=0)
description = models.TextField(max_length=1000)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
class Comment(models.Model):
post = models.ForeignKey('timeline.Photo', related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField(max_length=1000)
created_date = models.DateTimeField(default=timezone.now)
The related view:
def photo_detail(request, slug=None):
if not request.user.is_authenticated():
return HttpResponseRedirect("/accounts/login")
instance = get_object_or_404(Photo, slug=slug)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = instance
comment.save()
return redirect('timeline:detail', slug=instance.slug)
else:
form = CommentForm()
share_string = quote_plus(instance.description)
context = {
"title": instance.title,
"instance": instance,
"share_string": share_string,
"form": form,
}
return render(request, "photo_detail.html", context)
My forms.py:
class CommentForm(forms.ModelForm):
text = forms.CharField(widget=forms.Textarea, label='Leave a comment: ')
class Meta:
model = Comment
fields = [
"text",
]
Finally the template for the photo_detail view:
<div class="row">
<div class="col-md-12" id="comments">
<p>
{% if instance.comments.count == 0 %}
No Comments
{% elif instance.comments.count == 1 %}
{{ instance.comments.count }} Comment
{% else %}
{{ instance.comments.count }} Comments
{% endif %}
</p>
<hr style="margin-top: 10px;">
{% for comment in instance.comments.all %}
<div class="comment">
<div class="date pull-right">{{ comment.created_date | timesince }} Ago</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
<hr>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}
</div>
</div>
{% if user.is_superuser or user.is_authenticated %}
<div class="row">
<div class="col-md-12">
<form method="POST" class="comment-form" action=''>
{% csrf_token %}
{{ form | crispy }}
<button type="submit" class="comment-add btn btn-lg btn-purple">Add</button>
</form>
</div>
</div>
{% endif %}
Could anybody recommend the best approach for this? Any help would be very much appreciated! Thank You.
Using the ForeignKey is correct[1] - the missing piece is that you need to assign that in your view. After comment = form.save(commit=False) just add one line:
comment.author = request.user
and it will work.
[1] although you don't want the related_name as "Commenter" (because it refers to the way you access the comment from the user: the default is comment_set which makes more sense).