I'm trying to sort related items in a template by a field in a model three ForeignKey relationships away. I'm assembling the data for the template in the view as proposed in another StackOverflow answer:
Sorting related items in a Django template
As far as I can tell, I copied the code from this as-is except for I had to change variable names. It doesn't throw an error, it just displays no list items in the HTML unordered list.
# checkout.html
{% for item in cart_items %}
<tr>
<td class="left">
{{ item.name }}
<ul>
{% for part in part_list %}
<li>{{ part.name }}
{% endfor %}
</ul></td>
</tr>
{% endfor %}
And the view...
# views.py
def checkout(request):
cart_items = get_cart_items(request)
itemCollection = []
for item in cart_items:
item.part_list = item.product.buildpart.all().order_by('product__family__type')
itemCollection.append(item)
return render(request, 'checkout.html', locals())
And the get_cart_items function:
# cart.py
def get_cart_items(request):
""" return all items from the current user's cart """
return CartItem.objects.filter(cart_id=get_cart_id(request))
As I said, the template and view are pretty much copies of the solution presented in the aforementioned StackOverflow article. One thing I thought was curious was that itemCollection[] from the view is never referenced in the template.
I believe the order_by clause ('product__family__type') is right only because it doesn't generate an error. But in case that is the problem or a part of it here is the chain of models I am attempting to navigate in that order_by clause:
We start from the shopping cart model (CartItem):
class Item(models.Model):
cart_id = models.CharField(max_length=50)
quantity = models.IntegerField(default=1)
product = models.ForeignKey(PartModel, unique=False)
class Meta:
abstract = True
class CartItem(Item):
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['date_added']
verbose_name = "Cart Item"
Through the 'product' field we get to the model holding our inventory and its self-referential BuildPart ManyToMany model:
class PartModel(models.Model):
family = models.ForeignKey(PartFamily)
name = models.CharField("Model Name", max_length=50, unique=True)
buildpart = models.ManyToManyField('self', through='BuildPart',
symmetrical=False, related_name='+')
class Build(models.Model):
build = models.ForeignKey(PartModel, related_name='+')
part = models.ForeignKey(PartModel, related_name='+')
quantity = models.PositiveSmallIntegerField(default=1)
class Meta:
abstract = True
unique_together = ('build', 'part')
def __unicode__(self):
return self.build.name + ' with ' + str(self.quantity) + ' * ' + \
self.part.family.make.name + ' ' + self.part.name
class BuildPart(Build):
pass
class Meta:
verbose_name = "Build Part"
From there we follow the 'family' field to the PartFamily model:
class PartFamily(models.Model):
make = models.ForeignKey(PartMake)
type = models.ForeignKey(PartType)
name = models.CharField("Family Name", max_length=30,
unique=True)
slug = models.SlugField(unique=True)
And lastly, we get to the model with the 'order' field, the one we wish to sort the related items by, PartType:
class PartType(models.Model):
name = models.CharField("Part Type", max_length=30, unique=True)
slug = models.SlugField(unique=True)
order = models.PositiveSmallIntegerField()
description = models.TextField(blank=True, null=True)
To recap, how do I get the shopping cart products' related items, and sort them by the 'order' field in the PartType model?
You have two errors, both in the template.
Firstly, you've put your items with the sorted relationship in a list called itemCollection, but in the template you're iterating over cart_item instead. This is a very good example of why you should be explicit about what variables you pass to the template, rather than relying on locals().
Secondly, you then iterate over part_list without defining it. You mean item.part_list.
Related
I have a section residential interiors on the site, so the typical url looks like this https://caparolcenterspb.ru/decision/livingrooms/kitchen/provans/ (room and style)
However different rooms may have the same styles and when searching for styles in views.py it may output several styles and an error
views.py
selected_room = Room.objects.get(slug=rmslg)
style = Style.objects.get(slug=stslg)
When you try to replace slug with different styles depending on the room(for example, provans_kitchen), an error occurs in the template(just put provans by default will not work)
residentialInteriors.html
{% for room in all_rooms %}
<li class="menu__item menu__item_interiors">{{ room.name }}</li>
{% endfor %}
I have 2 solution ideas(preferably 2):
either change stslg in template by default to 'provans_' + str(room. slug), but this line does not work(especially not the fact that provans will be everywhere)
either search for style in views.py not only for stslg, but also for rmslg, but for this in the style model, you need to create a room field inherited from the room model, which also does not work for me, since Room is declared further than Style
models.py
class Style(models.Model):
name = models.CharField(max_length=30)
full_name = models.CharField(max_length=50)
slug = models.SlugField()
img = models.ImageField(upload_to='img/')
walls = models.TextField()
floor = models.TextField()
roof = models.TextField()
def __str__(self):
return self.full_name
class Meta:
verbose_name = 'Стили'
verbose_name_plural = 'Стили'
class Room(models.Model):
name = models.CharField(max_length=30)
slug = models.SlugField()
styles = models.ManyToManyField(Style)
def __str__(self):
return self.name
class Meta:
verbose_name = 'Комнаты'
verbose_name_plural = 'Комнаты'
Sounds like you want to use the relation field that you already have on Room
to only find styles associated with the given room?
selected_room = Room.objects.get(slug=rmslg)
style = selected_room.styles.get(slug=stslg)
I'm building an ecommerce app and I'd like to make a section that shows some featured items by category. I'm using three sliders that display those items; Each slider is a featured category and each item in the slider is a featured item.
The problem is that I can't figure out how to assign the item to the proper slider. For example: I want to assign a JeansJacket to "Clothes and accesories" and display it. I tried this:
{% for cat in categories %}
<h1>{{ cat.cat_name }}</h1>
<!--(carousel code in between)-->
<div class="carousel-inner" role="listbox">
{% for item in featured_items %}
{% if item.Categoría in cat.cat_name %}
{{ item }}
{% endif %}
This is a simplified version of what I have, without the rest of the content. I just can't figure out how to iterate though the featured items and display them in the corresponding category.
Edit: This is in models.py:
class Categorías(models.Model):
cat_name = models.CharField(max_length=30)
Destacado = models.BooleanField()
class Meta:
ordering = ('cat_name',)
verbose_name = 'Categoría'
verbose_name_plural = 'Categorías'
def __str__(self):
return self.cat_name
class publicaciones(models.Model):
Título = models.CharField(max_length=30)
Descripción = models.TextField(max_length=200)
Precio = models.FloatField()
Fotos = models.ImageField()
Categoría = models.ForeignKey(Categorías, on_delete=models.CASCADE)
Promocionado = models.BooleanField()
class Meta:
verbose_name = 'Publicación'
verbose_name_plural = 'Publicaciones'
def __str__(self):
return self.Título
You can use prefetch_related and Prefetch with a custom query to get all related articles for each category.
categories = Categorías.objects.prefetch_related(Prefetch(
'publicaciones_set',
queryset=publicaciones.objects.filter(Promocionado=True),
to_attr='featured_items'
))
Now you can loop over each category and then loop over this prefetch. You don't need to create a separate featured_items queryset
for category in categories:
for featured_item in category.featured_items:
...
You can use apply this pattern to your template
I have two models related them with a foreign key, pregunta is a question to a especific Comentario (comment).
models.py
class Comentario (models.Model):
titulo = models.CharField(max_length=50)
texto = models.CharField(max_length=200)
autor = models.ForeignKey (Perfil, null=True, blank=True, on_delete=models.CASCADE)
fecha_publicacion = models.DateTimeField(auto_now_add=True)
tag = models.ManyToManyField(Tags, blank=True)
def __str__(self):
return (self.titulo)
class Pregunta (models.Model):
descripcion = models.CharField(max_length=150)
autor = models.ForeignKey (Perfil, null=True, blank=True, on_delete=models.CASCADE)
fecha_pregunta = models.DateTimeField(auto_now_add=True)
comentario_preguntado = models.ForeignKey(Comentario, null=True, blank=True)
def __str__(self):
return (self.descripcion)
I create a view where I want to show only the comments having a question and the questions itself. I create two object list, one with the comments and the other with questions. The problem is that I want to show in the template the first comment and its questions, then the next comment and its questions...
I do not know if this should be done in the template or I need to change my view.
views.py
def responder(request):
comment = Comentario.objects.filter(id__in=Pregunta.objects.all().values_list('comentario_preguntado')).filter(autor=request.user)
question = Pregunta.objects.filter(comentario_preguntado__in=comment)
return render(request, 'home/responder.html', {'comment': comment, 'question': question})
Just access a commentario's preguntas via the related_name of their ForeignKey relation:
def responder(request):
# use __isnull to filter for comments with existing preguntas
# use prefetch_related to minimize db hits
# use plural name to be semantically sound ;)
comments = Comentario.objects\
.filter(pregunta_set__isnull=False, autor=request.user)\
.prefetch_related('pregunta_set')
return render(request, 'home/responder.html', {'comments': comments})
And in the template:
{% for comment in comments %}
# display comment stuff
{% for pregunta in comment.pregunta_set.all %}
# display pregunta stuff
{% endfor %}
{% endfor %}
Docs on related_name, prefetch_related and isnull.
Essence in the following.
I have a few different Magazine. And AutoForm from template shows all Articles and all Authors for possible selection (not only from current Magazine). How to restrict this choice of author a for article only from the current magazine? Can I do this using only the Template? If not, then how?
models.py
class Magazine(models.Model):
Name = models.CharField(max_length=200)
class Article(models.Model):
Name = models.CharField(max_length=200)
Magazine = models.ForeignKey(Magazine)
class Author(models.Model):
Name = models.CharField(max_length=200)
Magazine = models.ForeignKey(Magazine)
class Sets(models.Model):
Name = models.CharField(max_length=200)
Magazine = models.ForeignKey(Magazine)
Aut = models.ManyToManyField(Author, null=True, blank=True)
Set = models.ForeignKey(Article, null=True, blank=True)
forms.py:
class MagazineForm(ModelForm):
class Meta:
model = Magazine
fields = {'Name'}
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = {'Name'}
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = {'Name'}
class SetsForm(ModelForm):
class Meta:
model = Sets
fields = {'Name', 'Set', 'Aut'}
views.py
def building_details(request, magazine_id):
sets_form = SetsForm
args = {}
args.update(csrf(request))
args['magazine'] = Magazine.objects.get(id=magazine_id)
args['article'] = Article.objects.filter(Magazine=magazine_id)
args['author'] = Author.objects.filter(Magazine=magazine_id)
args['sets'] = Sets.objects.filter(Magazine=magazine_id)
args['setform'] = sets_form
return render_to_response('building.html', args)
Template
<form action='/add/{{ magazine.id }}/' method='post'>
{% csrf_token %}
{{ setform }}
<input type='submit' class='button' value="Добавить">
</form>
</div>
Maybe somewhere in this entry there are errors (I briefly edited, removing not playing a role here, essentially). In General I have everything working right, except that all objects are displayed, instead of only having the current.
Sounds like you are trying to restrict the options in the ForeignKey selector based on some criteria.
This means you need to override some of the details in the forms __init__ method:
class SetsForm(ModelForm):
class Meta:
model = Sets
fields = {'Name', 'Set', 'Aut'}
def __init__(self, *args, **kwargs):
articles = Article.objects.filter(Magazine=kwargs.pop('magazine_id'))
super(SetsForm, self).__init__(*args, **kwargs)
self.fields['articles'] = forms.ModelChoiceField(
queryset=articles ,
label="Articles",
)
Then call your form like so:
sets_form = SetsForm(magazine_id=magazine_id)
To get authors of articles only in the current magazine:
articles = Article.objects.filter(Magazine=magazine_id)
sets = Sets.objects.filter(Magazine=magazine_id, Set__in=articles)
import itertools
# build a flat list of all Authors in these sets (and call `set` to avoid duplicates)
authors = set(itertools.chain(*[s.Aut.all() for s in sets]))
This would be easier if there were a direct foreign key on Author to Article.
Also, you should follow Django convention by using lowercase field names. This will avoid confusion between fields and models, which use uppercase.
How do I get all the items associated with a part via the self-referential ManyToManyField? How to I fix my view to get part_list to contain a list of all the parts associated with 'product' and in the order specified by order_by?
# views.py
def productdetail(request, slug):
product = get_object_or_404(PartModel, slug=slug)
part_list = PartModel.objects.all().filter(buildpart__id=product.pk).order_by('family__type')
return render(request, 'productdetail.html', locals())
Here's the template:
# productdetail.html
<header>
<h1>'{{ product.name }}' Detail Page</h1>
</header>
<p>{{ product.name }}
<p>{{ product.slug }}
<p>{{ product.family }}
<p>{{ product.family.type }}
<p>{{ product.family.make }}
<p>${{ product.price }}
{% for part in part_items %}
<p>{{ part.name }}
{% endfor %}
Notice the PartModel model holding our inventory and its self-referential BuildPart ManyToMany model through the buildpart field:
class PartModel(models.Model):
family = models.ForeignKey(PartFamily)
name = models.CharField("Model Name", max_length=50, unique=True)
buildpart = models.ManyToManyField('self', through='BuildPart',
symmetrical=False, related_name='+')
class Build(models.Model):
build = models.ForeignKey(PartModel, related_name='+')
part = models.ForeignKey(PartModel, related_name='+')
quantity = models.PositiveSmallIntegerField(default=1)
class Meta:
abstract = True
unique_together = ('build', 'part')
def __unicode__(self):
return self.build.name + ' with ' + str(self.quantity) + ' * ' + \
self.part.family.make.name + ' ' + self.part.name
class BuildPart(Build):
pass
class Meta:
verbose_name = "Build Part"
To get everything in the right order with the order_by clause we follow the 'family' field to the PartFamily model:
class PartFamily(models.Model):
make = models.ForeignKey(PartMake)
type = models.ForeignKey(PartType)
name = models.CharField("Family Name", max_length=30,
unique=True)
slug = models.SlugField(unique=True)
And lastly, we get to the model with the 'order' field, the one we wish to sort the related items by, PartType:
class PartType(models.Model):
name = models.CharField("Part Type", max_length=30, unique=True)
slug = models.SlugField(unique=True)
order = models.PositiveSmallIntegerField()
description = models.TextField(blank=True, null=True)
I was able to find an answer to my own question with Roseman's help. The part_list line should be as follows:
# views.py
part_list = product.buildpart.all().order_by('family__type')
Once Roseman pointed out the variable name mismatch I did up the QuerySet as it made the most sense to me, and it works!