I have a many to many relation on models.
Product can have many images and image can assign to multiple productions.
this is my model type :
from django.db import models
class Image(models.Model):
image_id = models.AutoField(primary_key=True)
image_name = models.TextField(max_length=200)
class Product(models.Model):
product_id = models.AutoField(primary_key=True)
product_name = models.TextField(max_length=200)
image = models.ManyToManyField(Image, related_name="image")
and in view im filling model like this :
def home(self):
"""Renders the home page."""
assert isinstance(self, HttpRequest)
all_product = Product.objects.prefetch_related('image').all()
return render(
self,
'app/index.html',
{
'all_product': all_product
}
)
and in my template if i call model like this :
{% for each_model in all_product %}
<label> {{each_model.product_id}}</label>
<label> {{each_model.image.image_id}}</label>
{% endfor %}
only items shows from products not the joined value from manyTomany table.
{% for each_model in all_product %}
<label> {{each_model.product_id}}</label>
{% for image in each_model.image.all %}
{{ image.image_id }}
{% endfor %}
{% endfor %}
As I mentioned in my comment. You should loop over each_model.image and then access the image_id
Related
I can't figure out how to connect two models (product and images) in views and output images in html.
At the moment, several images are loaded for a specific project (for example, photos of a 3D model) all this through the admin panel. There are several projects, that is, 3D models, engineering and so on. And now I can't display 1 of those uploaded images on my site for each product (project). That is, either all the images that were uploaded are displayed (both 3d models and books and engineering).
Or if you use model.objects.first () displays the very first uploaded image( that is, the same for all projects).
My models:
class Portfolio (models.Model):
modeling='Modeling'
books ='Books'
engineering='Engineering'
category_ch = [
(modeling, 'Modeling'),
(books, 'Books'),
(engineering, 'Engineering'),
]
category = models.CharField('Category',max_length=100,choices=category_ch, default=modeling)
name = models.ForeignKey (Teams, related_name='maked', on_delete=models.PROTECT,blank=True)
short_discription = models.CharField("Name of the work", max_length=200, blank=True)
discription = models.TextField('Discription', blank=True)
сustomer = models.CharField('Customer', max_length=100, default='Заказчик')
created = models.DateTimeField('Date',auto_now_add=True)
class Meta:
verbose_name= 'Portfolio'
verbose_name_plural = 'Portfolios'
def __str__(self):
return self.short_discription
#def get_absolute_url(self):
#return reversed ('shop:product_detail', args=[self.category.slug, self.slug]')
class Image(models.Model):
image = models.ImageField('Picture of work', upload_to='products/%Y/%m/%d', blank=True)
product = models.ForeignKey(Portfolio, default=None, related_name='image', on_delete=models.PROTECT)
Views:
def portfolio_page(request):
portfolio = Portfolio.objects.all()
image_work= Image.objects.all()
ctx = {
'portfolio': portfolio,
'image':image_work,
}
return render(request, 'mainApp/portfolio_page.html',ctx)
HTML:
{% for el in portfolio %}
<div class="portfolio_db">
<h3> {{ el.short_discription }} </h3>
{% for i in image %}
<img class="photo_work" src="{{ i.image_work }}" alt="Oh, there is something wrong here" width="155px" height="215px"></img>
{% endfor %}
<h4> Maked by {{ el.name }} </h4>
<h4> Ordered by {{ el.сustomer }} </h4>
</div>
{% endfor %}
You can enumerate over the el.image_set.all in the template, so:
{% for el in portfolio %}
<!-- … -->
{% for i in el.image_set.all %}
<img class="photo_work" src="{{ i.image.url }}" alt="Oh, there is something wrong here" width="155px" height="215px"></img>
{% endfor %}
<!-- … -->
{% endfor %}
in the view, we can boost performance by fetching all related Images and do the JOINing at the Django/Python layer:
def portfolio_page(request):
portfolio = Portfolio.objects.prefetch_related('image_set')
ctx = {
'portfolio': portfolio
}
return render(request, 'mainApp/portfolio_page.html',ctx)
I'm trying to display a photographic session with all of it photos, so I have the session name in one model and I have the pictures in
another model linked by a foreign key. I'm not able to display it in
the HTML I'm not sure if I'm using the ListView get_context_data
correctly and I'm certainly sure the the html code is not correct but
I have not found how to do it.
views.py
class SessionPictures(generic.ListView):
model = PostSession
template_name = 'photoadmin/gallery.html'
def get_context_data(self, **kwargs):
context = super(SessionPictures, self).get_context_data(**kwargs)
context['picture'] = Images.objects.all()
return context
models.py
class PostSession(models.Model):
session_name = models.CharField(max_length=25)
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return str(self.session_name)
class Images(models.Model):
name = models.ForeignKey(
PostSession, related_name='images', on_delete=models.CASCADE, null=True, blank=True)
picture = models.ImageField(upload_to='pictures')
html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<h2>Images</h2>
<ul>
{% for session in object_list %}
<li>{{ session.session_name }}</li>
<ul>
<li>{{session.picture_set.all.url}}</li>
</ul>
{% endfor %}
</ul>
{% endblock %}
I'm expecting this:
Woods
picture1.url
picture2.url
picture3.url
Beach
Picture4.url
picture5.rul
As you already defined related_name="images" in Images model, so, session.images_set attribute won't work with PostSession.
class Images(models.Model):
name = models.ForeignKey(
PostSession,related_name='images', on_delete=models.CASCADE, null=True, blank=True)
Instead, use session.image.all in template(FYI: it returns a queryset, so you need to iterate through it to get the image object):
{% for session in object_list %}
<li>{{ session.session_name }}</li>
<ul>
{% for i in session.images.all %}
<li> i.picture.url </li>
{% endfor %}
</ul>
{% endfor %}
More information on reverse relation can be found in documentation.
i am creating an website where a user can search for recipes by their ingredients. I wish that when a user finally see recipe, ingredients there would be splited with ', ' in view. for now it is just space. I tried to do this in my model, but they i get error as in title
- 'CharField' object has no attribute split.
Models:
from django.db import models
class Ingredient(models.Model):
ingredient_name = models.CharField(max_length=250)
igredient_name1 = ingredient_name.split(', ')
def __str__(self):
return self.ingredient_name1
class Recipe(models.Model):
recipe_name = models.CharField(max_length=250)
preparation = models.CharField(max_length=1000)
ingredients = models.ManyToManyField(Ingredient)
def __str__(self):
return self.recipe_name
template:
<div>
<h1>Drink drank drunk</h1>
</div>
{% for drink in results %}
<div>
<p>{{ drink.recipe_name }}</p>
<p>Preparation: {{ drink.preparation }}</p>
<p>Ingredients:
{% for ingredient in drink.ingredients.all %}
{{ingredient.ingredient_name}}
{% endfor %}
</p>
</div>
{% endfor %}
view:
def drink_list(request):
template = "drinks/drink_list.html"
return render(request, template)
def search_results(besos):
query = besos.GET.get('q')
q = Q()
for queries in query.split(', '):
q |= (Q(ingredients__ingredient_name__icontains=queries))
results = Recipe.objects.filter(q)
template = "drinks/search_results.html"
context = {
'results' : results,
}
return render(besos, template, context)
After some mess understanding what is needed to accomplish it seems that the solution is just to add the desired comma into the template, modifying the forloop as follows:
{% for ingredient in drink.ingredients.all %}}
{{ingredient.ingredient_name}}{% if not forloop.last %},{% endif %}
{% endfor %}
I am trying to show a list of items organized by their category, with their respective category name directly above it in bold. For example:
Category 1
List item 1
List item 2
Category 2
List item 3
However, here is what the code currently does:
Category 1
List item 1
List item 2
List item 3
Category 2
List item 1
List item 2
List item 3
The two model classes involved taken from /models.py:
class Model(models.Model):
model_number = models.CharField('Model Number', max_length = 50)
manufacturer = models.ForeignKey('Manufacturer', on_delete = models.SET_NULL, null = True)
category = models.ForeignKey('Category', on_delete = models.SET_NULL, null = True)
description = models.TextField(max_length = 1000, help_text = "Enter brief description of product", null = True) #TextField for longer descriptions
def __str__(self):
return f'{self.model_number}..........{self.description}'
def get_absolute_url(self):
#Returns the url to access a particular location instance
return reverse('model-detail', args=[str(self.id)])
class Category(models.Model):
category = models.CharField('Equipment Type', max_length = 50,
help_text = "Enter general category of equipment")
class Meta:
verbose_name_plural = "categories" #default would have shown as "Categorys" on admin page
def __str__(self):
return self.category
/model_list.html
{% extends "base_generic.html" %}
{% block content %}
<div style="margin-left:20px; margin-top:20px">
<h1>Model list</h1>
</div>
{% if model_list %}
{% for category in model_list %}
<li><strong>{{ category.category }}</strong></li>
<ul>
{% for model in model_list %}
<li>
{{ model.model_number }}..........{{ model.description }}
</li>
{% endfor %}
</ul>
{% endfor %}
{% else %}
<p>There is no equipment in the database</p>
{% endif %}
{% endblock %}
For ForeignKey fields you can access one "Model"'s attributes from another's:
{% for model in models %}
{{ model.category.category }}
{{ model.category.help_text }}
{% endfor %}
Essentially - think of the field's for manufacturer and category that you have above in your Model as fields that are storing objects - once you get that field - your variable isn't a string or a integer, but it's an instance of the Category model. Then you can begin to select the attributes for that model, that is to say model.category is an instance of the Category object.
It looks like, from the way you have organised your models, that what you want to achieve is a little tricky. I would maybe advise looking at folding your categorisation of all your models in an array of objects of objects:
in views.py:
models = [ { 'category': 'categoryName', 'objects': [ SomeModelInstance1 , ... ] } ]
Then:
{% for model in models %}
model.category
{% for object in model.objects.all %}
{{ object.name }}
{% endfor %}
{% endfor %}
Let me know if this points you in the right direction...happy to help in the comments.
I am trying to display a checklist in the CreateView using the values in the ForeignKey fields for descriptions.
models.py
class Structure(models.Model):
name = models.CharField(max_length = 30)
description =models.CharField(max_length = 300, null=True, blank=True)
def __str__(self):
return self.name
class SelectedFramework(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
structure = models.ForegignKey(Structure)
selected = models.BooleanField(default = False)
views.py
class FrameworkCreateView(generic.CreateView):
model = SelectedFramework
fields =['structure', 'selected']
template_name = 'catalogue/structure.html'
def form_valid(self, form):
form.instance.user = self.request.user
return super(FrameworkCreateView, self).form_valid(form)
structure.html
{% extends 'catalogue\base.html' %}
{% block container %}
<h2>{% block title %}Structures{% endblock title %}</h2>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="col-sm-10">{{form.structure}} {{form.selected}}</div><br>
{% endfor %}
</div>
</form>
{% endblock %}
The code above works but will display the ForeignKey 'structure' as a dropdown list with the values of __str__. Is there a way to display string for structure.name and structure.description with the checkbox from selected in the CreateView?
In your template use:
{{ form.structure.name }}
{{ form.structure.description}}
You can write custom form, override the save method and create Structure object manually there:
class FrameworkForm(forms.ModelForm):
structure_name = forms.CharField(required=True)
structure_description = forms.CharField(required=False)
class Meta:
model = SelectedFramework
fields = [
'structure_name', 'structure_description', 'selected'
]
def save(self, commit=False):
instance = super(FrameworkForm, self).save(commit=False)
structure = Structure(
name=self.cleaned_data.get('structure_name'),
description=self.cleaned_data.get('structure_description')
)
structure.save()
instance.structure = structure
instance.save()
return instance
Also add form_class = FrameworkForm to your view instead of fields = ['structure', 'selected']
EDIT:
Perhaps you want something like this:
<ul>
{% for structure in form.fields.structure.choices.queryset %}
<li>{{ structure.name }} - {{ structure.description }}</li>
{% endfor %}
</ul>
If you want to get fields by iterating in the template. You have to use-
{% for field in form %}
{{ field }}
{% endfor %}
don't have to use any dot notation to get the field. If you want to get the label of the field you can use {{ field.label}} usually before {{field}}