I have three tables in my model:
class Recipe(models.Model):
title = models.CharField(max_length=200)
excerpt = models.TextField(null=True)
category = models.CharField(max_length=10, default='FoodGroup')
cookTime = models.CharField(max_length=20, default='15Min')
prepTime = models.CharField(max_length=20, default='5Min')
process = models.TextField(null=True)
slug = models.SlugField(max_length=100, unique=True)
updated = models.DateTimeField(auto_now=True)
published = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('recipe:single', args=[self.slug])
class Meta:
ordering = ['-published']
def __str__(self):
return self.title
class Ingredient(models.Model):
IngName = models.CharField(max_length=30)
IngType = models.CharField(max_length=20, null=True)
def __str__(self):
return self.IngName
class RecipeIngredients(models.Model):
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
recipe = models.ForeignKey(Core, on_delete=models.CASCADE)
And the view looks like this:
class RecipeView(ListView):
model = RecipeIngredients
template_name = 'core/recipes.html'
context_object_name = 'recipe'
I'm trying to then view this data using a for loop:
{% for recipe in recipe %}
{{ recipe.title }}
{% endfor%}
but nothing gets pulled using this method, I've tried looking at the docs but I'm unsure I'm doing this in the best way? I had a look at the many-to-many section of the Docs but I thought a link-table may be more appropriate for my scenario.
edit for anyone else having similar issues:
Using Django's ManyToMany model function ended up being more appropriate:
https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_many/
Answer was correct in saying object_name was poorly chosen.
The name of your iterable must be different from context_object_name.
Try different name like:
{% for diff_name in recipe %}
{{ diff_name.title }}
{% endfor%}
Related
I have a Blog Post Model and I have defined a function to calculate the no of likes.
The Model is as follows ->
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
title = models.CharField(max_length=255)
description = models.CharField(max_length=1000,null=True)
Tags = models.CharField(max_length = 255,null=True,blank=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
Likes = models.ManyToManyField(to=User, related_name='Post_likes')
def __str__(self):
return self.title
def likesCount(self):
return self.Likes.count()
Now I am querying the Post Model from the DB to get all the Posts as follows ->
posts = Post.objects.select_related().prefetch_related('images_set','comments_post').annotate(Count('comments_post')).all()
Here when I loop over the posts I can call the likesCount function and it gives me the correct result as well but I want to return the No of likes to the template.
How can I do that?
in your template, try this:
{{ post.likes_set.count }}
and please make the field names lowercase, they are not Classes
I am building a wiki and need to save every revision made for each wikipage. This means that i need a new revision tabel for every wikipage created.
When presenting each wikipage template with DetailView i need to access Wikipage.title, the latest revision and its Revision.content, Revision.author, Revision.last_edit and Revision.comment. I have been able to access the title, by setting "model=Wikipage" and revision, by setting "model=Revision" but not both at the same time.
models.py
class Wikipage(models.Model):
title = models.CharField(max_length=100)
date_created = models.DateTimeField('Created', auto_now_add=True)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "Wikipages"
class Revision(models.Model):
wikipage = models.ForeignKey(Wikipage, null=True,
on_delete=models.CASCADE, related_name='revision')
content = models.TextField('Content')
author = models.ForeignKey(User, null=True,
on_delete=models.SET_NULL)
last_edit = models.DateTimeField('Last edit', auto_now=True)
comment = models.TextField('Comment', blank=True)
class Meta:
verbose_name = 'Revision'
verbose_name_plural = 'Revisions'
ordering = ['-last_edit']
get_latest_by = ['last_edit']
def __str__(self):
return self.content
I wanted to use the DetailView and CreateView that comes with django, but I have not succeeded in accessing specific data from both tables.
I have gotten the ListView to work correctly, but that only needs the title from Wikipage, and nothing from Revision.
You can access the latest revision for a wikipage via wikipage.revision.latest(), since you correctly defined get_latest_by on the Revision model. You can do that directly in the template:
{% with wikipage.revision.latest as revision %}
{{ revision.last_edit }}
{{ revision.comment }}
{% endwith %}
On the DetailView you can access all the revisions using wikipage.revision where wikipage is the object of the DetailView and you could query the revisions to get the latest one.
I would recommend also this change
wikipage = models.ForeignKey(Wikipage, null=True, on_delete=models.CASCADE, related_name='revision')
to be
wikipage = models.ForeignKey(Wikipage, null=True, on_delete=models.CASCADE, related_name='revisions')
I have 2 models -
class InsName(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30, verbose_name = "Insurer/Broker")
alias = models.TextField(max_length=80, blank=True)
def __str__(self):
return f'{self.name}, {self.alias}'
def get_absolute_url(self):
return reverse('insurer-detail', args=[str(self.id)])
class Development(models.Model):
id = models.AutoField(primary_key=True)
logno = models.CharField(validators=[RegexValidator(regex='^(SCTASK|CDLI)[0-9]{7}', message='Please enter a valid log number', code='nomatch')], max_length=13)
insurer = models.ForeignKey(InsName, on_delete=models.SET_NULL, null=True, blank=False, verbose_name="Client")
policy = models.ManyToManyField(Policy, blank=True)
on my template I am outputting a list of Developments but where insurer is output I just want the name part to output. I need to retain the alias as it is used in other templates that also calls in InsName.
I thought I could use a substring before comma method in the template but I cant see that such a thing exists. Is this possible? If not any tips on how I can achieve this is greatly appreciated!
Maybe you can do it like this using F (apart from comment of #dirkgroten):
queryset = Development.objects.all().annotate(insurer_name=F('insurer__name'))
And use it in template:
{% for item in queryset %}
{{ item.insurer_name }}
{% endfor %}
Let's say we have 3 models:
class A(models.model):
name = models.CharField(max_length=30, unique=True)
class B(models.model):
name = models.CharField(max_length=30, unique=True)
a = models.OneToOneField(A, on_delete=models.CASCADE, primary_key=True)
class C(models.model):
name = models.CharField(max_length=30, unique=True)
b = models.ForeingKey(B, on_delete=models.CASCADE)
transaction_count = models.IntegerField(default=0)
and one view:
class AListView(generic.ListView):
model = A
In that view (template) I need to show: name of A, name of B and the last row (ordered by date) of "transactioncount" for each repository from b.
In my template I iterate over items in A and show them in following way:
{% for a in A %}
<tr>
<td>{{a.name}}</td>
<td>{{a.b.name}}</td>
<td>{{??? Don't know what to put here, to show the last row. I tried: a.b.c|last}}</td>
</tr>
{% endfor %}
I tried to build custom tags and use template functions like, but that unfortunately doesn't work:
{% with a.b.c_set.all|last as val %}
<td>val</td>
{% endwith}
Among my other tries would be to build a new queryset, but then I don't know how to assign items from model A to that queryset. I tried:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'A': A.objects.all(),
'c_data': C.objects.order_by('B', '-date').distinct(
'B')
})
)
What would be the best "pythonic" way to do this?
Thanks
First, of all, don't forget to set the back relation name like:
b = models.ForeingKey(B, on_delete=models.CASCADE, related_name='all_c')
Then:
class B(models.model):
name = models.CharField(max_length=30, unique=True)
a = models.OneToOneField(A, on_delete=models.CASCADE, primary_key=True)
#property
def last_c(self):
if self.all_c.exists():
return self.all_c.order_by('-date').last()
In your jinja template just write:
a.b.last_c
Have fun!
I'm working on a comic book database and there are main covers and variant covers. I have a page that shows all the Main covers, but I'd like to combine the variant covers too, in order of the publication date. This is what part of my models look like:
class Image(models.Model):
CATEGORY_CHOICES = (
('Cover', 'Cover'),
('Scan', 'Scan'),
('Other', 'Other'),
)
title = models.CharField(max_length=128)
number = models.CharField(max_length=20, help_text="Do not include the '#'.")
image = models.ImageField(upload_to="images/")
category = models.CharField(max_length=10, choices=CATEGORY_CHOICES)
### The variant cover is determined by the category_choice 'Cover'. ###
contributor = models.ManyToManyField(Contributor, blank=True, null=True)
date_added = models.DateField(auto_now_add=True, auto_now=True)
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
class Issue(models.Model):
CATEGORY_CHOICES = (
('Major', 'Major'),
('Minor', 'Minor'),
('Cameo', 'Cameo'),
('Other', 'Other'),
)
title = models.ForeignKey(Title)
number = models.CharField(max_length=20, help_text="Do not include the '#'.")
pub_date = models.DateField(blank=True, null=True)
cover_image = models.ImageField(upload_to="covers/", blank=True, null=True)
### This would be where the main image goes. ^^^ ###
images = models.ManyToManyField(Image, related_name="images_inc", blank=True, null=True)
### This is where the variant covers go.^^^ ###
has_emma = models.BooleanField(help_text="Check if Emma appears on the cover.")
My views.py for the main cover page looks like this:
def covers(request):
sort_by = request.GET.get('sort', 'pub_date')
if sort_by not in ['-date_added', 'date_added', '-pub_date', 'pub_date']:
sort_by = '-date_added'
issues = Issue.objects.filter(has_emma=True).order_by(sort_by).select_related(depth=1)
return render_to_response('comics/covers.html', {'issues': issues}, context_instance=RequestContext(request))
But I would like to display the variant covers too and not just the cover_image. Is there a way to do this? Maybe with something image and then filtering the category (of the Image model by cover)?
I, of course, can do this:
def variants(request):
Issue.objects.filter(has_emma=True).order_by(sort_by).select_related(depth=1)
images = Image.objects.filter(category='Cover').order_by('id')
return render_to_response('comics/variants.html', {'images': images}, context_instance=RequestContext(request))
But that does not give me enough flexibility as def covers does, and I want them combined and sorted by pub_date, like def covers.
Edit
models.py:
class Image(models.Model):
CATEGORY_CHOICES = (
('Cover', 'Cover'),
('Scan', 'Scan'),
('Other', 'Other'),
)
title = models.CharField(max_length=128)
image = models.ImageField(upload_to="images/")
category = models.CharField(max_length=10, choices=CATEGORY_CHOICES)
date_added = models.DateField(auto_now_add=True, auto_now=True)
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
class Issue(models.Model):
title = models.ForeignKey(Title)
number = models.CharField(max_length=20)
######
has_emma = models.BooleanField(help_text="Check if cover appearance.")
cover_image = models.ImageField(upload_to="covers/", blank=True, null=True)
images = models.ManyToManyField(Image, related_name="images_inc", blank=True, null=True)
######
def get_images(self):
''' Returns a list of all cover images combined,
"main" cover image first.
'''
images = [self.cover_image]
for image in self.images.filter(category='Cover'):
images.append(image.image)
return images
views.py:
def covers(request):
sort_by = request.GET.get('sort', '-pub_date')
if sort_by not in ['-date_added', 'date_added', '-pub_date', 'pub_date']:
sort_by = '-date_added'
issues = Issue.objects.filter(has_emma=True).order_by(sort_by)
return render_to_response('template.html', {'issues': issues,}, context_instance=RequestContext(request))
template.html:
{% for issue in issues %}{% for image in issue.get_images %}{{ image.image }}{% endfor %}{% endfor %} - displays nothing, however, {% for issue in issues %} {% for image in issue.get_images %} {{ issue.cover_image }} {% endfor %} {% endfor %} will repeatedly display the cover_image of the Issue model if there are variant covers, which are categorized in the Image model.
What can I do to fix this, so that it shows everything correctly? And for the record again, I want it to display the {{ cover_image }} (from the Issue model) and the {{ image.image }} as defined by the Image model combined.
If I understand your problem correctly, one way to solve it would be adding a method to Issue class like this:
class Issue(models.Model):
# fields...
def get_images(self):
''' Returns a list of all cover images combined,
"main" cover image first.
'''
images = [self.cover_image]
for image in self.images.filter(category='Cover'):
images.append(image.image)
return images
Then, in your template, you can do, for example, {% for image in issue.get_images %}....
(If it's not exactly what you need—then, I think, it would be better if you provide some template code as an example of what you're trying to achieve.)