Fields generated by inlineformset_factory disappears when validation fails - django

I have a simple Book Author relationship
class Author(models.Model):
first_name = models.CharField(max_length=125)
last_name = models.CharField(max_length=125)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Book(models.Model):
title = models.CharField(max_length=225)
author = models.ForeignKey(Author)
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
class BookForm(forms.ModelForm):
class Meta:
model = Book
This is what I have in my view
def addbook(request):
BookFormSet = inlineformset_factory(models.Author, models.Book, extra=1)
if request.method == 'GET':
author = models.AuthorForm()
books = BookFormSet()
else:
author = models.AuthorForm(request.POST)
if author.is_valid():
books = BookFormSet(request.POST)
if books.is_valid():
print(books)
return render_to_response('bookadd.html', locals(), context_instance = RequestContext(request))
My template looks like this
<form action="/books/add_new" method="post">
{% csrf_token %}
<table>
<tr>
<td>First name: </td>
<td>{{ author.first_name }}</td>
<td>{{author.first_name.errors}}</td>
</tr>
<tr>
<td>Last name</td>
<td>{{ author.last_name }}</td>
<td>{{author.last_name.errors}}</td>
</tr>
</table>
{{ books.management_form }}
{{ books.as_table }}
<br/>
<input type="submit" value="Submit" />
</form>
If I leave the title field blank and hit enter, on post back the book title field disappears, and I cannot figure out what to do about it. I want the field to be there with any data that was entered by the user.

You might try
author = models.AuthorForm(request.POST)
books = BookFormSet(request.POST)
if author.is_valid():
instead of
author = models.AuthorForm(request.POST)
if author.is_valid():
books = BookFormSet(request.POST)

Related

How can I capture the name or reg_no of the book in this list?

I'm working on a library system. I am unable to get the registration number of a book/books to be returned back to library...
My intention is to click on Return which captures the book name for return processing.. With what I have, when I print(book) it returns None meaning nothing has been taken from the click
My models
class Books(models.Model):
DEPARTMENT = (
('COM', 'Computer'),
('ELX', 'Electronics'),
('CIV', 'Civil'),
('BBS', 'Business'),
('MSC', 'Miscellaneous'),
)
reg_no = models.CharField(max_length=20, blank=True)
book_name = models.CharField(max_length=200)
no_of_books = models.IntegerField()
book_detail = models.TextField(default='text')
department = models.CharField(max_length=3, choices=DEPARTMENT)
def Claimbook(self):
if self.no_of_books>1:
self.no_of_books=self.no_of_books-1
self.save()
else:
print("not enough books to Claim")
def Addbook(self):
self.no_of_books=self.no_of_books+1
self.save()
def __str__(self):
return self.book_name
class Return(models.Model):
return_date = models.DateField(default=datetime.date.today)
borrowed_item = models.ForeignKey(Issue,on_delete=models.CASCADE)
def new_issue(request):
if request.method == 'POST':
i_form = IssueForm(request.POST)
if i_form.is_valid():
name = i_form.cleaned_data['borrower_id']
book = i_form.cleaned_data['book_id']
i_form.save(commit=True)
books = Books.objects.get(book_name=book)#Get a book names as selected in the dropdown
semest = Student.objects.get(name=name).semester#Get a student with a semester as selected in the dropdown
departm = Student.objects.get(name=name).depart
Books.Claimbook(books)
return redirect('new_issue')
else:
i_form = IssueForm()
semest = None
departm = None
sem_book = Semester.objects.filter(sem=semest, depart=departm)
return render(request, 'libman/new_issue.html', {'i_form': i_form, 'sem_book': sem_book})
The return view
def return_book(request):
book = request.GET.get('book_pk')
print(book)
books = Books.objects.get(id=book)
#b_id = r_form.cleaned_data['borrower_id']
Books.Addbook(books)
Issue.objects.filter(borrower_id=1, id=book).delete()
return render(request,'libman/view_issue.html',{'issue':issue})
The template that displays the borrowed books with a link to return beside each book.
{% if issue %}
<table class="layout">
<thead>
<th>Reg No.</th>
<th>Student Name</th>
<th>Book Name</th>
<th>Issue Date</th>
<th>Action</th>
</thead>
{% for borrow in issue %}
<tr>
<td>{{ borrow.borrower_id.student_id }}</td>
<td>{{ borrow.borrower_id }}</td>
<td>{{ borrow.book_id }}</td>
<td>{{ borrow.issue_date }}</td>
<td name='book_pk'>Return </td>
</tr>
{% endfor %}
</table>
{% else %}
<p> There are no books registered. </p>
{% endif %}
Issue model
class Issue(models.Model):
borrower_id = models.ForeignKey(Student,on_delete=models.CASCADE)
book_id = models.ForeignKey(Books,on_delete=models.CASCADE)
issue_date = models.DateField(default=datetime.date.today)
def __str__(self):
return str(self.book_id)
if i understood correctly - I believe you need to pass the borrow.book_id to the return view. so the return view knows which book you want return
in your template add the variable book_pk as follows
<td name='book_pk'>Return </td>
also you need to update your urls.py file to accept the new variable something like this
urlpatterns = [
path('returnbook/<book_pk>/', return_book),
]
but the above will need to also update your view function to handle the new passed argument and fetch the object etc..
def return_book(request,book_pk):
Or
you can add a form with a submit button
<form action="{% url 'return_book' %}">
<label for="book_id">Borrowed Book_id</label>
<input type="text" id="book_id" name="book_pk" value="{{ borrow.book_id }}" disabled><br><br>
<input type="submit" value="Submit">
</form>
it should work with your current code i think

Display number of views for a specific post for specific user in a table using django

i want to display number of views in a table for a specific post. I already have the data stored in db. it seems that print statement print('count', context['count_view']) is working inside get_context_data method but it is not working as expected in the template. Don't worry about the data inside the image, its actually dummy data. Anyone helpenter image description here
models.py
class ObjectViewed(models.Model):
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
ip_address = models.CharField(max_length=220, blank=True, null=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # User, Blog, or any other models
object_id = models.PositiveIntegerField() # User id, Blog id, or any other models id
content_object = GenericForeignKey('content_type', 'object_id')
timestamp = models.DateTimeField(auto_now_add=True)
views.py
class PostListView(ListView):
model = Post
template_name = 'edmin/post/postList.html'
context_object_name = 'posts'
ordering_by = ['-created']
def get_queryset(self):
post=Post.objects.filter(author=self.request.user)
return post
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
post=Post.objects.filter(author=self.request.user)
c_type = ContentType.objects.get_for_model(Post)
for p in post:
context['count_view'] = ObjectViewed.objects.filter(content_type=c_type, object_id=p.id).count()
print('count',context['count_view'])
return context
postList.html
{% for post in posts %}
{% if post.status == 'Draft' %}
{% else %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td><a style="color:blue" href="{% url 'edmin:post_detail_view' pk=post.pk %}">{{ post.title }}</a></td>
<td>{{ post.banner_title }}</td>
<td>{{ post.created }}</td>
<td>{{ count_view }}</td>
<td>{{ post.status }}</td>
<td>Edit</td>
<td>Delete</td>
</tr>
{% endif %}
{% endfor %}
Since context allows 'dict', you can pass all of your views through context.

Working with checkboxes and querying the database in Django

My goal is to create a page that lists all the courses available in the database and have the user select which courses they would like to be a tutor for.
I have a CustomUser model, a courses model, and finally a TutorTeachesCourse model that takes user and courses as foreign keys.
# model.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
is_tutee = models.BooleanField(default=False)
is_tutor = models.BooleanField(default=False)
courses = models.ManyToManyField(Courses)
class Courses(models.Model):
course_name = models.CharField(max_length=100, null = False)
course_number = models.CharField(max_length=100, null = False)
department = models.ForeignKey(Department, on_delete=models.CASCADE)
course_description = models.CharField(max_length=1000, blank=True)
#tutor = models.ManyToManyField(CustomUser) #moved m2m relationship to user model
objects = models.Manager()
def __str__(self):
return self.course_name
# forms.py
class EditTutoredCoursesForm(forms.Form):
model = CustomUser
course = forms.ModelMultipleChoiceField(
queryset = Courses.objects.all(),
widget = forms.CheckboxSelectMultiple,
)
def clean(self):
cleaned_data = super(EditTutoredCoursesForm, self).clean()
is_tutor = cleaned_data.get('is_tutor')
if not is_tutor:
raise forms.ValidationError('Validation error.')
def save(self,commit=True):
rel=super(EditTutoredCoursesForm,self).save(commit=False)
rel.is_tutor=self.cleaned_data['is_tutor']
if commit:
rel.save()
return rel
# views.py
def edit_tutored_courses(request):
user = request.user
if request.method == 'POST':
form = EditTutoredCoursesForm(request.POST)
if form.is_valid():
user.courses.set(form.cleaned_data['courses'])
user = form.save(commit=True)
messages.success(request, 'Success!')
return redirect(reverse('view_profile'))
else:
form = EditTutoredCoursesForm()
context = {
'form' : form,
}
return render(request, 'edit_tutored_courses.html', context)
And here the page where the user selects/unselects the courses they wish to tutor/not tutor.
# edit_tutored_courses.html
<table style="width:50%">
<tr>
<th>Course Name</th>
</tr>
<form method="POST" action="">
{% csrf_token %}
{% for is_tutor in form %}
{% for course in is_tutor %}
<tr>
<td>{{ course }}</td>
<td>{{ user }}</td>
</tr>
{% endfor %}
{% endfor %}
</table>
<input type="submit" value="Save Changes"/>
</form>
I can display the courses on my page but I don't know how to make changes to the database. I want the checkboxes to mean that once I click "submit" the table TutorTeachesCourses populates with that user with the checked courses, and if I uncheck the boxes it means it deletes the existing one. (That means I also need to make the page automatically check the boxes that exists int he database. How do I do all of this?

How to create a dynamic filter based on the primary key of ForeignKey in ListView?

I am relatively new to Django but the main problem I am facing right now is to create a ListView that will display uploaded documents based on the primary key of my ForeignKey.
I have tried several methods of trying to create the filter and read the online documentation on class-based view but it does not seem to have relevant information on how to use the primary key of my ForeignKey in my filter.
These are my models:
class Post(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(default = 'default0.jpg',
upload_to='course_image/')
description = models.TextField()
price = models.DecimalField(decimal_places=2, max_digits=6)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField(default = 0)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk' : self.pk})
class Lesson(models.Model):
title = models.CharField(max_length=100)
file = models.FileField(upload_to="lesson/pdf")
date_posted = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson_upload', kwargs={'pk': self.pk})
Here is my ListView with the filter that is not working:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
# def get_queryset(self):
# return Lesson.objects.filter(Post__pk=self.Post.pk)
def get_queryset(self):
self.post__pk = get_object_or_404(post__pk,
name=self.kwargs['post__pk'])
return Lesson.objects.filter(post__pk=self.post__pk)
Here is my urls.py:
path('post/<int:pk>/lesson_uploaded/', LessonListView.as_view(), name='lesson_uploaded'),
Here is my html:
{% extends "store/base.html" %}
{% block content %}
<div id="main">
<table class="table mb-0">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Download</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for lesson in lesson %}
<tr>
<td>
{% if lesson.file %}
<img src="{{ lesson.file.url }}" style="width:100px;">
{% else %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
You can try like this:
In urls, add post_id :
path('lessons/<int:post_id>/', LessonListView.as_view()),
Then update the View to get the post_id in get_queryset method:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
def get_queryset(self):
return Lesson.objects.filter(post_id=self.kwargs.get('post_id'))
Also, please don't name list and item of that list in a for loop same, so update it to:
{% for l in lesson %}. // or anything other than lesson
<tr>
<td>
{% if l.file %}

Django forms field not appearing on webpage

Fields I have added in django forms are not visible on webpage.
Attached model, view and html for the reference below.
This is an additional filed which I intent to add to the forms, I am new to Django and learning by enhancing the current project.
"estimated_headcount" is the new filed I have added in the forms.
Thanks
Model
class EstimatedHeadcount(models.Model):
count = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Meta:
default_permissions = []
#staticmethod
def __gotoadmin__():
return True
forms.py
class ClientProfileForm(forms.ModelForm):
class Meta:
model = ClientProfile
fields = ('full_name', 'short_name', 'account_payable',
'require_job_number', 'currency', 'segment', 'market', 'estimated_headcount', 'is_technicolor',
'address')
views.py
def client_profile(request):
all_profiles = ClientProfile.objects.filter(status='active')
profile = None
pid = request.GET.get('pid')
client_profile_form = ClientProfileForm()
if pid:
profile = ClientProfile.objects.get(id=pid)
client_profile_form = ClientProfileForm(instance=profile)
if request.method == 'POST':
client_profile_form = ClientProfileForm(request.POST, instance=profile)
if client_profile_form.is_valid():
profile = client_profile_form.save()
profile.csv_mapping = profile.full_name
profile.save()
if profile:
for task_type in TaskType.objects.all():
if not profile.task_costs.filter(task_type=task_type):
task_cost = TaskCost(task_type=task_type)
task_cost.save()
profile.task_costs.add(task_cost)
return render(request, "prod/client_profile.html", {'all_profiles': all_profiles,
'profile': profile,
'client_profile_form': client_profile_form})
clientprofile.html
<div class="content">
<form id='add_new_client_form' method="post" action="">
{% csrf_token %}
<table class="table">
<tbody>
{{ client_profile_form.as_table }}
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button class="lock" type="button"
onclick="unlock(this, '#add_new_client_form')">Unlock
</button>
<button type="submit">SAVE</button>
</td>
</tr>
</tfoot>
</table>
</form>
</div>
As far as I can tell from your code, there is no relation between the ClientProfile model and the EstimatedHeadcount model.
estimated_headcount should be a field on the ClientProfile model.
class ClientProfile(models.Model):
...
estimated_headcount = models.CharField(max_length=100)
Side note: I would expect the estimated headcount to be a numeric value, so an IntegerField or PositiveIntegerField might be a better choice.