Formset inside a ModelForm - django

So lets try to explain:
I have a model called inventory, which serves to handle my products and their quantity. And I have another model that I would ask to do a certain service and would need to inform how many products and their quantity would be needed to complete that same service.
My problem is, I am using a ModelForm to render my form, but with this I just can select one product in the field items(that is a foreignkey of Inventory) and can't inform how many items would be needed. So, I'd like something to my user select one or more products from my inventory and inform how many them he will need to complete de service. I read about formset and seems to be what I need, but i can't figure out how to put in my code, or inside of my actually modelForm.
inventory models:
class Inventory(models.Model):
name= models.Charfield(max_lenght=100)
description = models.TextField()
quantity = models.PositiveIntegerField()
......
service models:
class Service(models.Model):
name = models.Charfield(max_length=100)
items = models.ForeignKey(Inventory, on_delete=models.DO_NOTHING)
description = models.TextField()
date= models.DateField(default=datetime.date.today)
....
views:
class CreateServiceView(CreateView):
model = Service
form_class = ServiceModelForm
template_name = 'service/create_service.html'
success_url = reverse_lazy('service:list_service')
template:
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<form method="post">
{%csrf_token %}
<table class="table">
{{ form }}
</table>
<tr>
<td>
<a href="service:list_service">
<button type="submit" class="btn btn-primary">Save</button>
</a>
<a href="#">
<button type="reset" class="btn btn-primary">Cancel</button>
</a>
</td>
</tr>
</form>
{% endblock %}
I need to change this field to something which I would select the item and put the quantity.

Based on how you named your variable items with a "s", I derive that you mean to use the same service for multiple inventories, right?
In that case you cannot use items = models.ForeignKey(Inventory, ...) in Service. Rather use service = models.ForeignKey(Service, ...) in Inventory.
This way every Inventory object is related to exactly one Service object. You can then access all the inventory-objects of a specific service by doing service.inventory_set.all()

Related

How to properly display all inline fields associated with its parent model fields when paginating in Django template?

I'm a student and a Django newbie, and we have a project that we're trying to build using Django. In my journey to building the project I stumbled upon a problem and got stuck for weeks now.
I want to display all the inline fields associated with its parent field on one page as I paginate. When I tried to paginate a model with 2 additional models that have foreign keys to it I got a weird result in my template. I can't seem to figure out how to fix it. I tried several methods on the Internet and have read numerous forums and discussions but to no avail, none has worked so far. Below are my files and a few Images:
(models.py)
from django.db import models
class History(models.Model):
BARANGAY = (
('Alegria','Alegria'),
('Bagacay','Bagacay'),
('Baluntay','Baluntay'),
('Datal Anggas','Datal Anggas'),
('Domolok','Domolok'),
('Kawas','Kawas'),
('Ladol','Ladol'),
('Maribulan','Maribulan'),
('Pag-Asa','Pag-Asa'),
('Paraiso','Paraiso'),
('Poblacion','Poblacion'),
('Spring','Spring'),
('Tokawal','Tokawal')
)
barangay_name = models.CharField(max_length=100,choices=BARANGAY,default='Alegria')
barangay_img = models.ImageField(upload_to='history_imgs',blank=True)
barangay_info = models.TextField()
class GeoHazard(models.Model):
history = models.ForeignKey(History,related_name='geohazards',on_delete=models.CASCADE)
geohazard_img = models.ImageField(upload_to='history_imgs',blank=True)
date_published = models.CharField(max_length=100, null=True)
geohazard_info = models.TextField()
class Assessment(models.Model):
RATINGS = (
('HIGH','HIGH'),
('HIGH (Mitigated)','HIGH (Mitigated)'),
('MODERATE','MODERATE'),
('MODERATE (Mitigated)','MODERATE (Mitigated)'),
('LOW','LOW'),
('UNKNOWN','UNKNOWN'),
)
history = models.ForeignKey(History,related_name='assessment',on_delete=models.CASCADE)
purok_name = models.CharField(max_length=50)
purok_coordinates = models.CharField(max_length=100,default='unknown')
flood_rating = models.CharField(max_length=100,choices=RATINGS,default='UNKNOWN')
landslide_rating = models.CharField(max_length=100,choices=RATINGS,default='UNKNOWN')
In my models.py I have the parent model 'History' and two additional models 'GeoHazard' and 'Assessment' both having foreign keys.
admin-dashboard.png
(admin.py)
from django.contrib import admin
from auxiliary.models import (
History,
GeoHazard,
Assessment
)
class GeoHazardInline(admin.StackedInline):
model = GeoHazard
extra = 0
class AssessmentInline(admin.StackedInline):
model = Assessment
extra = 0
class HistoryAdmin(admin.ModelAdmin):
inlines = [GeoHazardInline,AssessmentInline]
admin.site.register(History,HistoryAdmin)
In my admin.py I am using 'StackedInline'. I structured it this way so that the parent model 'History' can have multiple inline fields associated with it.
(views.py #1)
class history(ListView):
model = History
template_name = 'auxiliary/history.html'
context_object_name = 'histories'
paginate_by = 1
Initially, I used 'ListView' to take advantage on its pre-built pagination method 'paginate_by' but by doing so the template resulted to this (see image below). As u guys can see the inline fields are paginated_by '1' as well, and the other inline fields got separated from the first page.
template-views1.png)
(views.py #2)
class HistoryView(ListView):
model = History
template_name = 'auxiliary/history.html'
context_object_name = 'histories'
paginate_by = 1
def get_context_data(self, **kwargs):
context = super(HistoryView, self).get_context_data(**kwargs)
context.update({
'geohazards': GeoHazard.objects.all(),
'assessments': Assessment.objects.all()
})
return context
So I tried a different approach; now by having 3 models passed in my 'ListView' by overriding the 'context' using get_context_data. In this approach all the inline fields are displayed in my template template-views2-A.png , but this time it raises a new issue, even though all the inline fields are displayed in the template It wasn't with their associated parent fields. Now when selecting a new page in my pagination buttons the parent field changes template-views2-B.png
but the inline fields remain the same.
In addition, I also tried 'GeoHazard.objects.filter(history_id=1)' when updating the 'context' dictionary but this is not the solution, since this only grabs the inline fields from the parent fields with the specific id. Then I tried to use custom template tags, django custom template-tags but it didn't work.
(template.html)
Here's my template btw:
{% for history in histories %}
<div class="row m-0">
<div class="col-md-3 col-sm-12 mt-4">
<div class="card bg-transparent border-0">
<div class="car-body text-center">
<h3><u>{{ history.barangay_name}}</u></h3>
<img src="{{ history.barangay_img.url }}" width="180" height="180" class="rounded-circle">
</div>
</div>
</div>
<div class="col-md-8 col-sm-12 mt-4" style="display: grid;place-items:center;">
<div class="card bg-transparent border-0">
<div class="car-body">
<p style="text-align:justify;">{{ history.barangay_info}}</p>
</div>
</div>
</div>
</div>
<hr>
{% endfor %}
{% if geohazards %}
{% for hazard in geohazards %}
<div class="row m-0">
<div class="col-md-3 col-sm-12 mt-4">
<div class="card bg-transparent border-0">
<div class="car-body text-center">
<img src="{{hazard.geohazard_img.url}}" height="200" width="300">
</div>
</div>
</div>
<div class="col-md-8 col-sm-12 mt-4" style="display: grid;place-items:center;">
<div class="card bg-transparent border-0">
<div class="car-body">
<h4>{{hazard.date_published}}</h4>
<p style="text-align:justify;">{{hazard.geohazard_info}}</p>
</div>
</div>
</div>
</div>
{% endfor %}
<hr>
template-with-labels.png
In this pic I labeled each fields that I'm trying to display in my template.
Been at it for ages and looking forward to anyone who can help. Really eager to find the solution to this for the Project’s deadline is right at our doorsteps. Thanks in advance!
You should be able to access the related GeoHazard and Assessment objects by their related_name:
{% for history in histories %}
{{ history.barangay_name}
{# other history information #}
{% for hazard in history.geohazards.all %}
{{ hazard.geohazard_info }}
{# other hazard information #}
{% endfor %}
{% for assessment in history.assessment.all %}
{{ assessment.purok_name }}
{# other assessment information #}
{% endfor %}
{% endfor %}
Accessing the attribute that is defined by related_name will return an instance of a RelatedManager that has methods like all() (same as objects on a model).
Note that for this to work you don't need to add any additional stuff in the context, but performance-wise it might make sense to use prefetch_related() otherwise for every History instance additional queries are performed to fetch the related objects.
class HistoryView(ListView):
model = History
template_name = 'auxiliary/history.html'
context_object_name = 'histories'
paginate_by = 1
def get_queryset(self):
histories = super().get_queryset()
histories = histories.prefetch_related("geohazards", "assessment")
return histories

how can i use two types of queryset models override save method?

i want to ask you if i POST an article with featured=True then the old featured=true get transferred to featured=false queryset. i just want two [:2] values in featured=true queryset. here is the example of what i want
there are two article in featured=true queryset which i want the second article automatically get updated to featured=false when i create new article.
the output of my code when i applied is featuring only one article. were as i want two articles to be featured.
Here is the query of database with print(featured_articles.count()), photo of the list of articles in the admin, and the template.
models.py
class ArticleQuerySet(models.Manager):
def get_queryset(self):
return super(ArticleQuerySet,self).get_queryset().filter(status=True)
class Article(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(
User, on_delete=models.CASCADE, blank=True, null=True)
thumbnail = models.ImageField(default='def.jpg',
upload_to='article/thumbnails')
timestamp = models.DateTimeField(auto_now_add=True)
content = RichTextUploadingField(blank=True)
featured = models.BooleanField(default=False)
status = models.BooleanField(default=True)
tags = TaggableManager()
objects = models.Manager()
status_objects = ArticleQuerySet()
def __str__(self):
return self.title
class Meta:
ordering = ['-timestamp']
# i also try this method
def save(self, *args, **kwargs):
if self.featured == True:
Article.objects.filter(pk__in=(Article.objects.filter(featured=True,
).values_list('pk',flat=True)[:2])).update(featured=False)
self.featured = True
super(Article, self).save(*args, **kwargs)
views.py
from django.shortcuts import render
from .models import Article
def index(request):
featured_articles = Article.status_objects.filter(tags__exact='1', featured=True)[:2]
regular_articles = Article.status_objects.filter(tags__exact='1').exclude(pk__in=featured_articles)
context = {
'featured': featured_articles,
'regular': regular_articles,
}
return render(request, 'news/index.html', context)
index.html
<!--post header-->
<div class="post-head">
<h2 class="title"> Article </h2>
</div>
<!-- post body -->
<div class="post-body">
<div class="">
<!-- item one -->
<div class="item">
<div class="row">
<div class="col-sm-6 main-post-inner bord-right">
{% for nat in featured %}
<article>
<figure>
<a href="{% url 'news-detail' pk=nat.pk %}"><img src="{{ nat.thumbnail.url }}"
height="242" width="345" alt="" class="img-responsive"></a>
</figure>
<div class="post-info">
<h3>{{ nat.title }}
</h3>
<span>
<i class="ti-timer"></i>
{{ nat.timestamp | timesince }}
</span>
<p></p>
</div>
</article>
{% endfor %}
</div>
<div class="col-sm-6">
<div class="news-list">
{% for nat in regular %}
<div class="news-list-item">
<div class="img-wrapper">
<a href="{% url 'news-detail' pk=nat.pk %}" class="thumb">
<img src="{{ nat.thumbnail.url }}" alt="" class="img-responsive">
<div class="link-icon">
<i class="fa fa-camera"></i>
</div>
</a>
</div>
<div class="post-info-2">
<h5>{{ nat.title }}
</h5>
<i class="ti-timer"></i> {{ nat.timestamp | timesince }}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Your code looks fine, but the standard way of doing this is keeping all marked as featured as is, without marking previous featured articles as featured=False:
views.py
def articles(self):
featured_articles = Article.status_objects.filter(tags__exact='1', featured=True)[:2]
regular_articles = Article.status_objects.filter(tags__exact='1').exclude(pk__in=featured_articles)
...
Note how .exclude(featured=True) is not in the regular_articles queryset. Old featured articles will automatically rollover into this queryset.
Some advantages of this approach are:
No need to maintain the list of featured articles
You get to keep a list of previously featured articles
You don't run into race conditions when multiple articles are being added/featured
You don't bother the database with updates
Your code is easier to maintain and read
Updating a single article doesn't have side effects (e.g. when you update an article, you know for sure you are only updating that article, nothing else)
That being said, there are times when you actually want to mark them as featured=false, but unless you have a compelling reason to, it's usually better to keep things simpler.
There is more better way for this. Create One new model like this.
remove featured field from your article model.
class featured (models.Model):
featured=models.OneToOneField(Article, related_name='featured', on_delete=models.CASCADE )
create as much featured article you want to featured.
suppose you want only two featured article, then create only two record.
whenever you want to change your featured article just update that record.
i hope this will be simple design and very flexible also you can increase featured article as much as you want by just adding new record and decrease as much as you want by just delete.
here you don't want to override your article save method.

When I add() to a ManyToMany relationship, does it replace the previous records?

I'm creating a set of view for an app that adds and edits teams and volunteers. Volunteers can take part of as many teams as they wish, and a few of them are set as team leaders. Due to the need for flexibility of the process (I guess), I've left both items as separated (I'm open to suggestions on this, though!) and I'm trying to set consistency on the views. For example, I want to make sure that every time I add a team leader on one view, they are also enrolled as a team member.
class Team(models.Model):
team_name = models.CharField(max_length=30, null=True, blank=True)
leaders = models.ManyToManyField(Volunteer, related_name='leaders', blank=True)
enrolled = models.ManyToManyField(Volunteer, related_name='enrolled', blank=True)
class Volunteer(models.Model):
name = models.CharField(max_length=30)
I have a view on which I'm doing just this. After saving the form, I take each of the leaders and add them to the enrolled field. However, when I add them, I find that the previous values of enrolled on the field has been replaced by the new values I added. I could just save the previous values in a variable and reapply, but is it supposed to work this way?
class TeamUpdate(LoginRequiredMixin, UpdateView):
form_class = TeamCreateForm
model = Team
def form_valid(self, form):
self.object = form.save()
for i in self.object.leaders.all():
self.object.enrolled.add(i)
return HttpResponseRedirect(reverse('single', args=(self.object.id,)))
Can you point me in the right direction?
Edit: adding the form and template. The former shows all the actual fields in the model (I had reduced them in order to simplify it for you, but I can post it all if you prefer).
class TeamCreateForm(forms.ModelForm):
class Meta:
model = Team
template_name = 'team_form.html'
fields = '__all__'
Template:
{% extends "base.html" %}
{% block title %}Add or edit team{% endblock %}
{% block content %}
<div class="container">
<div class="container">
<h1 class="blue-text">add or edit team</h1>
<form class="registration" action="" method="post">{% csrf_token %}
{% csrf_token %}
<div class="white row z-depth-1">
<small class="red-text">{{ form.non_field_errors }}</small>
<div class="col s9">
<label for="id_name">Team Name</label>{{ form.team_name }}
<small class="error brick-text">{{ form.team_name.errors }}</small>
</div>
<div class="col s12">
<label for="id_name">Team Leaders</label>{{ form.leaders }}
<small class="error brick-text">{{ form.leaders.errors }}</small>
</div>
<a class="waves-effect waves-light btn-large blue" onClick="$(this).closest('form').submit();">Save</a>
</div>
</form>
</div>
</div>
{% endblock %}
Your HTML form does not include enrolled field, but your ModelForm does include it. Since the ModelForm does not receive any data in the enrolled field, it'll assume that you explicitly removed all selected volunteers and remove them.
You need to explicitly define which fields are in your ModelForm:
class TeamCreateForm(forms.ModelForm):
class Meta:
model = Team
template_name = 'team_form.html'
fields = ['team_name', 'leaders']
It is recommended to always explicitly name the fields instead of using __all__. If you don't and you add a field that shouldn't be edited by users, it can become a security issue.

Django formset access initial data in template

I have a Django formset with a set of initial data which loads a foreignkey relation object into the initial form:
{{ cellcountformset.management_form }}
{% for form in cellcountformset %}
<div id="">
{{ form.errors }}
{{ form.as_p }}
</div>
{% endfor %}
The relevant models look like this:
class CellType(models.Model):
readable_name = models.CharField(max_length=50)
machine_name = models.CharField(max_length=50)
comment = models.TextField(blank=True)
class CellCount(models.Model):
cell_count_instance = models.ForeignKey(CellCountInstance)
cell = models.ForeignKey(CellType)
normal_count = models.IntegerField()
abnormal_count = models.IntegerField()
comment = models.TextField(blank=True)
I want to be able to display the machine_name of the cell referred to by the cell attribute of the CellCount model as the #id of the div. I use a ModelFormSet for the CellCount, which is passed a list of CellType objects as its initial data.
The form's initial data is stored in form.initial, so try:
{{ form.initial.cell.machine_name }}
I don't think that you can use the form fields to traverse models and get at the machine name, but I'll check. If you can, the syntax will be something like
<div id="{{ form.fields.cell.machine_name }}">
But I think that you are going to need to write a custom template filter for this. It can just take the form as an argument, and return the machine name associated with it (or blank if the form is unbound). Then you will be able to write
<div id="{{ form|machine_name }}">

Django - Models FK/many2many relationships

I need to understand a bit better how do FK/m2m relationships work.
I've prepared Images model for uploading images and it has an additional feature - it can be categorized by adding to a specific gallery (m2m relation to gallery).
To access gallery name I just had to do a query set for example:
Images.objects.filter(gallery__gallery_name = '')
I'd like to reverse the query a little bit so from Gallery model I can access pictures which are in specific gallery (gallery_name).
How I can do that?
Models:
class Images(models.Model):
image = models.ImageField(upload_to=update_filename, blank=True, null=True, verbose_name="Obrazek")
gallery = models.ForeignKey('Gallery', blank=True, null=True)
class Gallery(models.Model):
gallery_name = models.CharField(max_length=128)
gallery_description = models.TextField(blank=True, null=True)
View:
def index(request):
p = Gallery.objects.filter(gallery_name="main").order_by('-id')
return TemplateResponse(request, 'gallery.html',
{'gallery': p,
},)
Template:
{% for n in gallery.all %}
<h2 class="center">{{n.gallery_name}}</h2>
<hr>
{% for n in gallery.images_set %}
<div class="grid_4">
{{ n.image }}
</div>
{% endfor%}
Try something along the lines of:
# models.py
class Gallery(models.Model):
name = models.CharField(max_length=30)
description = models.CharField(max_length=200, null=True)
images = models.ManyToMany(Image)
class Image(models.Model):
title = models.CharField(max_length=30)
caption = models.CharField(max_length=200, null=True)
image = models.ImageField(upload_to=SOMEPLACE_FOR_MEDIA)
From here you should be able to do things like:
image = Image.objects.get(title="Girl Holding Cheese")
related_galleries = image.gallery_set.all()
or something similar as needed to pull what you want. The same goes the other way. To pull all images in a gallery you would do
gallery = Gallery.objects.get(name="Cheesy Wimmin")
related_images = gallery.images.all()
Though the assignments at the end aren't necessary, I usually just pass gallery.images.all() or image.gallery_set.all() directly. Note the "_set" at the end of the reference from the object that does not contain the M2M definition.
On the subject of direct usage, you can do compound references like
Image.objects.get(title="Girl Holding Cheese").gallery_set.all()
as well, but you have to decide when this makes code more clear and concise and when it just makes it more confusing to read later.
I hope this put you in the right direction.
Update
In your comment below you noticed that you cannot do
images = Images.objects.filter(gallery_set="Cheesy Wimmins")
related_galleries = images.gallery_set.all()
This is because you would be trying to filter() or all() on a queryset, not an individual model. So to make this work you can use a for loop in your template. Something like
# views.py
galleries = Gallery.objects.all()
return render(request, 'some/template.html', {'galleries': galleries})
And then
<!-- templates/some/template.thml -->
{% for gallery in galleries %}
<div class="gallery">
<h2>{{ gallery.name }}</h2>
{% for item in gallery.images.all %}
<div class="image">
{{ item.image }}
</div>
{% endfor %}
</div>
{% endfor %}
or something like this. Of course, you need to do whatever formatting steps you want to make this look right, but that's a way to get at your data.
The problem is with the {% for n in gallery.images_set %} bit in your template. images_set is a related manager, not a queryset. To get a queryset, you need to call all or another of the DBAPI methods that return querysets. So, just change it to gallery.images_set.all, and you're good.
gallery is a QuerySet - it doesn't have a images_set.
This is where naming your variables more appropriately can easily start preventing these problems: for example, galleries would be more appropriate for a list of Gallery objects... then, galleries.images_set would immediately raise red flags.
Anyways, you need to call images_set on what you've called n
{% for n in gallery.all %}
<h2 class="center">{{n.gallery_name}}</h2>
<hr>
{% for n in n.images_set.all %}
<div class="grid_4">
{{ n.image }}
</div>
{% endfor%}