I need help with solving an issue regarding duplicate database query's for each form in an inlineformset. I have a page where users can add and edit books belonging to an author.
models.py
from django.db import models
class Author(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class Book(models.Model):
id = models.AutoField(primary_key=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
category_idcategory = models.ForeignKey(Category, models.DO_NOTHING)
class Category(models.Model):
name = models.CharField(max_length=100)
forms.py
from django import forms
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = '__all__'
views.py
instance = get_object_or_404(Author, id=id)
form = inlineformset_factory(Author, Book, form=BookForm, can_delete=True, extra=5)
formset = form(request.POST or None, instance=instance)
if request.method == "POST":
if formset.is_valid():
instanceForm = formset.save(commit=False)
for obj in instanceForm:
obj.save()
for obj in formset.deleted_objects:
obj.delete()
return HttpResponseRedirect(URL)
When I run the template, it performs a database query to the Category model for each form in formset. How do I prevent those duplicates? I dont know where to put select_related or prefetch_related. If instances of Book model grows to a large number, the page load times are getting very slow.
template.html
<table class="table table-sm">
{{ formset.management_form }}
<thead>
<td>Title</td>
<td>Category</td>
<td>delete</td>
</thead>
<tbody>
{% for obj in formset %}
{{ obj.id }}
<tr>
<td>{{ obj.title }}</td>
<td>{{ obj.category_idcategory }}</td>
<td>{{ obj.DELETE }}</td>
</tr>
{% endfor %}
</tbody>
</table>
You can change the queryset of a formset like this:
class InlineBookFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Here is where to put the select_related.
self.queryset = Book.objects.select_related('category_idcategory').all()
then in your factory method call:
# Use your custom InlineBookFormSet
form = inlineformset_factory(Author, Book, form=BookForm, can_delete=True, extra=5, formset=InlineBookFormSet)
Related
I am new to Django and Python,
I have a table for clients, and one for trips. I can display the clients on the clients page, and the same for the trips.
But on the clients page, I want to show all the trips that are linked to each client. And this is where I hit a wall.
This is my models.py
from django.db import models
from django.core.validators import RegexValidator
# Create your models here.
class Clientes(models.Model):
nome = models.CharField(max_length=30)
apelido = models.CharField(max_length=30)
morada = models.CharField(max_length=200)
tel = models.CharField(max_length=9, validators=[RegexValidator(r'^\d{1,10}$')])
nif = models.CharField(max_length=9, validators=[RegexValidator(r'^\d{1,10}$')])
def __str__(self):
return "%s %s" % (self.nome, self.apelido)
class Meta:
verbose_name_plural = "Clientes"
class Viagem(models.Model):
trip_id = models.CharField(max_length=30)
cliente = models.ForeignKey(Clientes, on_delete=models.CASCADE)
comp = models.CharField(max_length=30)
data = models.DateField()
destino = models.CharField(max_length=30)
def __str__(self):
return self.trip_id
class Meta:
verbose_name_plural = "Viagens"
This is my views.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Clientes, Viagem
# Create your views here.
def index(request):
ls= Clientes.objects.all()
context = {'ls': ls}
return render(request, "booking/home.html", context)
def cliente(request, id):
ls= Clientes.objects.filter(id=id)
context = {'ls': ls}
return render(request, "booking/cliente.html", context)
def trip(request):
ls= Viagem.objects.all()
context = {'ls': ls}
return render(request, "booking/trip.html", context)
and this is the table on the home.html
<table id="selector" class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Nome</th>
<th>Apelido</th>
<th>Morada</th>
<th>Telemóvel</th>
<th>NIF</th>
<th>Viagens</th>
</tr>
</thead>
<tbody>
{% for ls in ls %}
<tr>
<td>{{ls.nome}}</td>
<td>{{ls.apelido}}</td>
<td>{{ls.morada}}</td>
<td>{{ls.tel}}</td>
<td>{{ls.nif}}</td>
<td>{{ls.trip_id}}</td>
</tr>
{% endfor %}
</tbody>
</table>
I assume it has something to do with ForeignKey, but a ForeignKey on the first class won't work.
I thought about creating a new def on the views.py using the Viagem table and a diferent context, but that also doesn't seem to be the solution.
So, anyone can point me in the right direction?
In your views.py, specifically the cliente function, you want to add the following line:
viagems=Viabem.objects.filter(cliente=ls)
modify the following line:
ls= Clientes.objects.filter(id=id)
so that it now shows:
ls=Clientes.objects.get(id=id)
and then change your context to equal:
context = {'ls': ls, 'viagems': viagems}
then you will be able to iterate over the different viagems in your html template file with the following kind of structure:
{% for viagem in viagems %}
{{ viagem.whatever_field }}
{% endfor %}
and that should be it, I believe...
Since you are filtering by ID (fetching single object), this is one of the options to go:
views.py
from django.shortcuts import get_object_or_404
def cliente(request, id):
ls = get_object_or_404(Clientes, id=id)
trips = obj.viagem_set.all()
context = {'ls': ls, 'trips': trips}
return render(request, "booking/cliente.html", context)
template.html
<table id="selector" class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Nome</th>
<th>Apelido</th>
<th>Morada</th>
<th>Telemóvel</th>
<th>NIF</th>
<th>Viagens</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ls.nome}}</td>
<td>{{ls.apelido}}</td>
<td>{{ls.morada}}</td>
<td>{{ls.tel}}</td>
<td>{{ls.nif}}</td>
<td>{% for trip in trips %}{{ trip.id }}{% endfor %}</td>
</tr>
{% endfor %}
</tbody>
While I'm not sure why you need to show record ID's in the table.
#João de Sousa, I found you want to show all trips that are associated with each client.
First of all you have to get a single client by his id obj = clients.objects.get(id = id) then this id pass to referred model like this obj1 = Viagem.objects.filter( id = obj.id)
According to my understanding from your above posted question is that you want to get help about how you get data from reference model/table. Django provides very easy and flexible way to get data from tables.
Feel free to ask any question if you need more clarification.
I'm creating a website for general education uses and one of the features is a calendar section where I would like the user to be able to store their events but then when they login they can view the events they created. This is for an academic assignment.
I can't seem to get this calendar to link to a user field. I would like each record in the calendar table to have a user_id which will be the same as the current_user. I understand that you can user the django default user auth but i have not done that due to not knowing about it and by the time I realized it was too late.
So all I really need to do is when the form is valid, I need to assign the value of the current_user id as the value for student in the calendar table. But for some reason I keep getting problems and the form isn't detected as being valid or the program just doesn't assign the value.
My main objective is to have each user view their own calendar. I don't mind changing the current student field to a foreign key field
Student.Views
def calendar(request):
student_obj = Student.objects.get(student_name=current_user)
print(student_obj)
if request.method == 'POST':
form = EventsForm(initial={'student': '3'} or request.POST)
print(form.errors)
if form.is_valid():
form.save()
all_events = Events.objects.filter(student=student_obj.id)
messages.success(request, 'Event Has Been Added')
return render(request, 'Student/calendar.html', {'all_events': all_events})
else:
messages.success(request, 'Event Has NOT Been Added')
all_events = Events.objects.filter(student=student_obj.id)
return render(request, 'Student/calendar.html', {'all_events': all_events})
Student.Models
class Student(models.Model):
student_name = models.CharField(max_length=59, default=None)
username = models.CharField(max_length=59)
password = models.CharField(max_length=59)
age = models.PositiveIntegerField()
date_of_birth = models.DateField(max_length=10)
form = models.CharField(max_length=3)
email = models.EmailField(max_length=59)
present = models.PositiveIntegerField(default=0)
late = models.PositiveIntegerField(default=0)
absent = models.PositiveIntegerField(default=0)
maths_grade = models.PositiveIntegerField(default=0)
english_grade = models.PositiveIntegerField(default=0)
science_grade = models.PositiveIntegerField(default=0)
behaviour_level = models.PositiveIntegerField(default=0)
def __str__(self):
return self.student_name
class Events(models.Model):
student = models.CharField(max_length=200)
date = models.DateField(max_length=10, default=None)
event = models.CharField(max_length=200)
completed = models.BooleanField(default=False)
Student.forms
class EventsForm(forms.ModelForm):
class Meta:
model = Events
fields = ["event", "completed", "date", "student"]
Calendar Template
<div class="container" style="color: #fff">
<br/>
{% if messages %}
{% for message in messages %}
<div class="alert alert-warning alert-dismissible" roles="alert">
<button class="close" data-dismiss="alert">
<small>x</small>
</button>
{{ message }}
</div>
{% endfor %}
{% endif %}
{% if all_events %}
<div>
<table class="table table-dark table-bordered">
{% for things in all_events %}
{% if things.completed %}
<style>
.striker {
text-decoration: line-through;
text-color: black;
}
</style>
<tr class="table-danger striker">
<td style="color: black">{{ things.date }}</td>
<td>{{ things.event }}</td>
<td><center>Delete</center></td>
</tr>
{% else %}
<tr>
<td>{{ things.date }}</td>
<td >{{ things.event }}</td>
<td><center>Delete</center></td>
</tr>
{% endif %}
{% endfor %}
</table>
</div>
{% endif %}
Change form.save() to
event = form.save(commit=False)
event.student = request.user
event.save()
That'll do it.
Don't make it complicated, this have to be easier. If I understood what are you trying to do I would create my models as following:
#model.py
from django.db.models.signals import post_save
from django.dispatch import receiver
class Student(models.Model):
user= models.OneToOneField(User, null=True) #student is user
#Define student fields...
class Calendar(models.Model):
#Defile calender fields here along with its default values ...
user = models.OneToOneField(Student)
'''
this signal populate calender table with user_id along with its
calender fields
'''
#receiver(post_save, sender=Student)
def create_student_calender(sender, instance, created, **kwargs):
if created and not kwargs.get('raw', False):
calendar = Calender(user=instance)
calendar.save()
class Event(models.Model):
calender = models.ForeignKey(Callender, null=True)
# Define event fields here ...
With models structure like this, whenever student create account Calendar model will be populated saved with calendar fields and student's id field.
So how do student create their events? Simply create an a inlineformset_factory form to create events on each student's calendar like this:
#form.py
from django.forms import inlineformset_factory
from django import forms
class EventForm(forms.ModelForm):
#Events form fields here...
class Meta:
model = Event
fields = ["Event's field"]
EventFormSet = inlineformset_factory(Calender, Event, form=EventForm,
max_num=1, can_delete=False)
Render this formset with a view for student to create their events,the formset automatically associate student's event to calendar.
So you can add your logic to do more.
I am facing issues in saving data to my sqlite db using Django.
When i am accessing 127.0.0.1/personal/makeentry--> the page shows the form , but when i click on submit after entering the details, the detils are not saved in db.
Below is the code
model.py
from django.db import models
class Account(models.Model):
accountId = models.IntegerField(primary_key=True,unique=True,
blank=False, null=False, auto_created=True)
accountName = models.CharField(max_length=100)
countryName = models.CharField(max_length=100)
marketName = models.CharField(max_length=100)
msdmName = models.CharField(max_length=100)
deliveryManagedFrom = models.CharField(max_length=100)
location = models.CharField(max_length=100)
def __str__(self):
return self.accountName
admin.py
from django.contrib import admin
from .models import Account
# Register your models here.
class AccountAdmin(admin.ModelAdmin):
list_display = ['accountId', 'accountName','countryName', 'marketName',
'msdmName','deliveryManagedFrom','location']
admin.site.register(Account, AccountAdmin)
forms.py
from django import forms
from . models import Account
from django.forms import ModelForm
class AccountForm(forms.Form):
accountName = forms.CharField(label="Account Name", max_length=100)
countryName = forms.CharField(label="Country Name", max_length=100)
marketName = forms.CharField(label="Market Name", max_length=100)
msdmName = forms.CharField(label="MSDM Name", max_length=100)
deliveryManagedFrom = forms.CharField(label="Delivery Managed From", max_length=100)
location = forms.CharField(label="Location", max_length=100)
class AccountForm(ModelForm):
class Meta:
model = Account
fields = ['accountName','countryName', 'marketName', 'msdmName', 'deliveryManagedFrom', 'location']
views.py
from django.shortcuts import render
from django.views import generic
from .models import Account
from .forms import AccountForm
def index(request):
return render(request, 'personal/home.html')
# generic view to fetch the data then show in a list
class IndexView(generic.ListView):
# a name to refer to the object_list(to be used in the index.html)
context_object_name = 'account_list'
template_name = 'personal/index.html'
def get_queryset(self):
return Account.objects.all()
# generic view to show the details of a particular object
class DetailsView(generic.DetailView):
model = Account
template_name = 'personal/detail.html'
def makeentry(request):
if request.method == 'POST':
form = AccountForm(request.POST)
if form.is_valid():
accountName = request.POST.get('Account Name', '')
countryName = request.POST.get('Country Name', '')
marketName = request.POST.get('Market Name', '')
msdmName = request.POST.get('MSDM Name', '')
deliveryManagedFrom = request.POST.get('Delivery Managed From', '')
location = request.POST.get('Location', '')
account = Account(accountName=accountName, countryName=countryName, marketName=marketName, msdmName=msdmName, deliveryManagedFrom=deliveryManagedFrom, location=location)
account.save()
form = AccountForm()
return render(request, 'personal/makeentry.html', {'form': form})
else:
form = AccountForm()
return render(request, 'personal/makeentry.html', {'form': form})
makeentry.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Account Entry</title>
</head>
<body>
<form action="{% url 'makeentry' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
</body>
</html>
detail.html
{% if account_list %}
<table>
<tr>
<td> Account Name </td>
<td> Country Name </td>
<td> Market Name </td>
<td> MSDM Name </td>
<td> Delivery Managed From </td>
<td> Location </td>
</tr>
{% for account in account_list %}
<tr>
<td>{{ account.accountName }}</td>
<td>{{ account.countryName }}</td>
<td>{{ account.marketName }}</td>
<td>{{ account.msdmName }}</td>
<td>{{ account.deliveryManagedFrom }}</td>
<td>{{ account.location }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
I think you are making this harder than it needs to. You have a Form and a ModelForm of the same name. You should just be able to use the ModelForm. If there are attributes you want to pass with your ModelForm check out ModelForm Overriding default widgets. It has also been my experience to set action=“” and let the view handle the redirect on success.
def make_entry(request):
if request.POST:
form = AccountForm(request.POST)
if form.is_valid():
new_form = form.save()
return redirect('where-you-want-to-redirect ',
# View current saved info or change to what you want to show
pk=new_form.pk)
I am trying to post multiple values from different models in a for loop in a template. It is not doing what I am planning. I want to display a count of LeadActions that belong to Leads in a table. I did comment the part out that is not working as well. The table should display the list of Leads and the the count of how many overdue actions(LeadActions) there are for that specific lead.
My View
class LeadListView(LoginRequiredMixin, generic.ListView):
login_url = '/scrty/login/'
template_name = "nodiso/leadslist.html"
model = models.Leads
def get_context_data(self, **kwargs):
ctx = super(LeadListView, self).get_context_data(**kwargs)
ctx['actions']= models.LeadActions.objects.all()
return ctx
def get_queryset(self):
return models.Leads.objects.filter(company=self.request.session['compid'],archive=False)
My template
<table class="table">
<thead>
<th>Name</th>
<th>Overdue Tasks</th>
<th>Total Tasks</th>
</thead>
{% for lead in leads_list %}
{# {% for action in action_list %}#}
<tr>
<td>{{lead.name}}</td>
<td><span class="badge">{{ actions.name|length }}</span></td>
<td><span class="badge">42</span></td>
</tr>
{# {% endfor %}#}
{% endfor %}
</table>
The Models
class LeadActions(models.Model):
lead = models.ForeignKey(Leads)
name = models.CharField(max_length=265)
crdate = models.DateField(auto_now_add=True)
Duedate = models.DateField()
creator = models.CharField(max_length=265)
overdue = models.IntegerField(null=True,blank=True)
def __str__(self):
return self.name
class Leads(models.Model):
company = models.ManyToManyField(Company)
user = models.ManyToManyField(settings.AUTH_USER_MODEL)
name = models.CharField(max_length=265)
email = models.EmailField(max_length=265)
tel = models.IntegerField()
archive = models.BooleanField(default=False)
dateenq = models.DateField(auto_now_add=True,null=True)
def get_absolute_url(self):
return reverse('nodisoapp:leadlist')
def __str__(self):
return self.name
You shouldn't be sending the list of actions from the view. Instead, in the template, you can access {{ lead.leadactions_set.count }} to give the count of LeadActions related to each Lead in the loop.
I'm new to Django. I am making a simple store.
Currently I am working on the Order section.
Every Order has Order Items inside it. Every order item has some values and a product id.
What I am trying to display on the index.html, is the orders and its items inside it. However order.items always outputs order.OrderItem.None
views.py
class IndexView(generic.ListView):
template_name = 'order/index.html'
context_object_name = 'all_orders'
def get_queryset(self):
return Order.objects.all().prefetch_related('items')
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
return context
views.py
# Create your models here.
class Order(models.Model):
user = models.ForeignKey(User, related_name='orders')
created_at = models.DateTimeField(auto_now_add=True, null=True)
class OrderItem(models.Model):
product = models.ForeignKey(Product)
order = models.ForeignKey(Order, related_name='items')
item_name = models.CharField(max_length=255, null=True, blank=True)
item_price_in_usd = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
def __str__(self):
return self.product.name
index.html
{% for order in all_orders %}
<tr>
<td>{{ order}}</td>
<td>{{ order.created_at}}</td>
<td>{{ order.items}}</td>
</tr>
{% endfor %}
Ok, I have found to solution. Apparently you have to add .all
{% for order in all_orders %}
<tr>
<td>{{ order}}</td>
<td>{{ order.created_at}}</td>
<td>
{% for items in order.items.all %}
<td>{{ items.item_name}}</td>
{% endfor %}
</td>
</tr>
{% endfor %}