django - having trouble selecting values based on FK relationship in views.py - python

I have two models that look like;
class Body(models.Model):
body_id = models.AutoField(primary_key=True)
is_adult = models.BooleanField(default=False)
body = models.TextField()
add_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
add_date = models.DateTimeField()
edit_user = models.CharField(max_length=25, blank=True, null=True)
edit_date = models.DateTimeField(blank=True, null=True)
category = models.ForeignKey(Category, models.CASCADE)
class Meta:
managed = True
db_table = 'jdb_body'
class BodyTag(models.Model):
body_tag_id = models.AutoField(primary_key=True)
body = models.ForeignKey('Body', models.CASCADE)
tag = models.ForeignKey('Tag', models.CASCADE, db_column='tag')
class Meta:
managed = True
db_table = 'jdb_body_tag'
def __str__(self):
return self.tag
I have a view that looks like;
def index(request):
latest_body_list = Body.objects.all().order_by('-body_id')
context = {
'latest_body_list': latest_body_list
}
return render(request, 'index.html', context)
That view gives me a list Body records no problem. I am trying to display Body records with their corresponding BodyTag records. What am I doing wrong?

You neeed a ManyToManyField in your class Body
tags = models.ManyToManyField('Tag')
To access
body = Body.objects.get(body_id=1)
tags = body.tags.all()

EDIT
My previous answer was incorrect because I did not see the more complex relationship. The problem is this: You have many BodyTag objects to one Body object, but you also have many BodyTag objects to one Tag object. So as shown in the image, BodyTag's BT4 and BT5 belong to Body's BODY1 and BODY2. That's why #chipchap31 is talking of a ManyToMany relationship.
If this is not the relationship you want, and from your comments I do not think that you want a ManyToMany field, then the BodyTag model should be either changed, or perhaps discarded. Perhaps relate Body to Tag directly using the ForeignKey, and then have a field in the Tag model that distinguishes it with the type of tag it is, so one type would be body, and you can use a choices field to show all the different types of Tags.
Previous answer (Incorrect)
If you mean displaying these in your template, then all you have to do is follow the ForeignKey relationship backwards. This is shown in the documentaion for the views, but it would look pretty much the same in the template. Something like:
{% for body in latest_body_list %}
{{ body }}
{% for tag in body.tag_set.all %}
{{ tag }}
{% endfor %}
{% endfor %}
The -set is what tells django to look backward in the ForeignKey relationship.
A perhaps better way, also shown in the documentation would be to define a related_name in your ForeignKey:
class BodyTag(models.Model):
body_tag_id = models.AutoField(primary_key=True)
body = models.ForeignKey('Body', models.CASCADE, related_name='tags')
tag = models.ForeignKey('Tag', models.CASCADE, db_column='tag')
Then your template could be written a little better:
{% for body in latest_body_list %}
{{ body }}
{% for tag in body.tags.all %}
{{ tag }}
{% endfor %}
{% endfor %}

Related

Django models referencing each other

I have two models and I want to put an if condition in my template to get the desired output.
I have the first model
class Subject(models.Model):
name = models.CharField(max_length=50)
books = models.ForeignKey('Books')
And the second one
class Books(models.Model):
name = models.CharField(max_length=50)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
And a view
def View(request):
subjects = Subject.objects.all()
return render('my_html.html",{'subjects':subjects})
In my model, I have wanted to do this.
{% for subject in subjects %}
{% if subject.books.count > 0 %}
{{ subject.name }}
{% endif %}
{% endfor %}
I thought the only way is to have the models referencing each other for my plan to work but I also think that looping will require more resources. Is there a better way of doing this either thro' the view or just in the models?
You only need to ForeignKey from Book to Subject, so:
class Subject(models.Model):
name = models.CharField(max_length=50)
# no books
class Books(models.Model):
name = models.CharField(max_length=50)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
then you can filter the subjects such that it only retrieves Subjects with at least one book:
def view(request):
subjects = Subject.objects.filter(books__isnull=False).distinct()
return render(request, 'my_html.html', {'subjects': subjects})
this will make a LEFT OUTER JOIN on the table of the Book model, and check if there is at least one book. You thus do not need to filter in the template (which is not a good idea anyway).

How do i query for comment in Django

I am new in Django and I have been following tutorials online. I am having problem on how to display the comments.
How do i query for comments in views, so i can display comments for a particular post.
Model:
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
image_caption = models.TextField(blank=True, null=True)
class Comments (models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
commented_image = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, blank=True)
comment_post = models.TextField()
Views.py:
def home_view(request):
comment = Comments.objects.all() #This is getting all comment in all post, how do i query for comment in a particular post.
context{'comment':comment}
return render(...)
Template:
{% for com in comment %}
<p>{{ com.comment_post }}</p>
{% endfor %}
You can do
post = Post.objects.get(id=1)
comment = post.comments_set.all()
Following Relationships "Backward"
If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased. This Manager returns QuerySets, which can be filtered and manipulated as described in the “Retrieving objects” section above.
Note this behaviour can be overridden.
You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries'), the above example code would look like this:
Edit #2:
views.py:
def home_view(request):
posts = Post.objects.all().reverse()[5]
context{ 'posts': posts, }
return render(...)
Now in your templates you can do something like:
{% if posts %}
{% for post in posts %}
{{ post.image_caption }}
{% for comment in post.comments_set.all %}
{{ comment.comment_post }}
{% endfor %}
{% endfor %}
{% endif %}
First of all, take a look at Django queryset documentation, especially select_related for this kind of issues (to reduce number of queries to database). I didn't try but following snippet must work.
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
image_caption = models.TextField(blank=True, null=True)
class Comments (models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
commented_image = models.ForeignKey(Post, related_nam="comments", on_delete=models.CASCADE)
comment_post = models.TextField()
...
def home_view(request):
post = Post.objects.filter(id=request.data.get('post_id')).select_related("comments") # specify the post anyhow ..
comments = post.comments
context{'comment': comments}
return render(...)
def home_view(request):
particular_Post= Post.objects.get(id=1)
comment = Comments.objects.get(Post=particular_Post)
context{'comment':comment}
return render(...)
to understund Query in django i suggest U to start by
python manage.py shell
and Import you're Models

How to add tags using taggit in Django admin

I am trying to add tags to my blog app using django admin but every-time I add a tag through the admin console, I get this error Can't call similar_objects with a non-instance manager
Is this because I am not saving the the tags in the admin or is this because I implemented taggit wrongly?
This is where I define my view and display and article, I try to grab similar articles using taggit
def article(request, slug):
article = Post.objects.filter(slug=slug).values()
article_info = {
"articles": article,
"related_articles" : Post.tags.similar_objects()
}
return render(request, 'article.htm', article_info)
Update
This is how my post model looks like
STATUS = (
(0,"Draft"),
(1,"Publish")
)
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.CharField(max_length=200, unique=True)
author_biography = models.TextField(default="N/A")
updated_on = models.DateTimeField(auto_now= True)
content = models.TextField()
upload_image = models.ImageField(default="default.png", blank=True)
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
tags = TaggableManager()
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
Right, I see the problem.
You are trying to access tags on the class Post and not an instance of Post. However, you're also using filter() and values() and the variable name is singular, so I think there's perhaps a misunderstanding there as well.
I assume the kind of thing you want to do is this;
def article(request, slug):
article = Post.objects.get(slug=slug)
article_info = {
"article": article,
"related_articles" : article.tags.similar_objects() # Instance of Post is article
}
return render(request, 'article.htm', article_info)
Usually, this type of structure is made using ManyToMany relationships.
Going this way, your code should look like this:
class Tag(models.Model):
name = models.CharField(max_length=255)
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
tag = models.ManyToManyField(Tag)
To add a tag to a post:
post_obj = Post.objects.get(title="le title")
tag = Tag.objects.get(name="le name")
post_obj.tag.add(tag)
To query:
post_obj = Post.objects.get(title="le title")
tags_in_post = post_obj.tag.all()
This should facilitate your page rendering. Assuming you want to display every post and, within it, every that, your code should be something like this:
// in the end of the view function:
return render(request, 'article.htm', context={'post': article_info})
// inside the template:
{% for p in post %}
<li> <span> {{p.title}} </span>
{% for tag in post.tag %}
<ul> {{tag.name}} </ul>
{% endfor %}
</li>
{% endfor %}

Using the 'for' loop in django tags for 'ForeignKey'

My django loop in the templates does not work correctly. Why, because of me, everything looks good?
Any help will be appreciated.
My models.py
class Tags(models.Model):
name = models.CharField(max_length=10)
class Person(models.Model):
keywords = models.ForeignKey(Tags, on_delete=models.CASCADE)
My views.py
def person_detail(request, user_id):
person = get_object_or_404(Person, pk=user_id)
context = {'person': person}
return render(request, 'person_detail.html', context)
My templates.html
{% for tag in person.tags %}
<span class="badge badge-lg badge-pill badge-info"># {{ person.tags.name } </span>
{% endfor %}
This gives no results even though there is data in the database.
You are doing it backwards. Your Tags model should have a foreign key to Person, not the other way around. For example:
class Person(models.Model):
...
class Tag(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
name = models.CharField(max_length=10)
Note that I've also renamed your Tags model to Tag as it makes sense to name models singular.
Then you can write the following:
{% for tag in person.tag_set.all %}
...
{% endfor %}

How to create a form field for every foreignkey in manytomany relationship in Django

I don't understand how to build a specific form in Django.
First of all here are my models:
class Category(models.Model):
name = models.CharField(max_length=200, unique=True)
class Assessment(models.Model):
name = models.CharField(max_length=200)
pub_date = models.DateTimeField(verbose_name=_('date published'), default=timezone.now)
classgroup = models.ForeignKey(ClassGroup, verbose_name=_('class'), on_delete=models.CASCADE, related_name='+')
category = models.ManyToManyField(Category, through='AssessmentScale', through_fields=('assessment', 'category'),)
total = models.IntegerField()
class AssessmentScale(models.Model):
assessment = models.ForeignKey(Assessment, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
value = models.IntegerField()
I would like to have a form like this html form. Actually, an assessment scale is sub-divided into different categories. So when I create an assessment, I'd like have a form field for each category allowing to add a value via my custom intermediary model AssessmentScale. But I really don't know the Django way to build this form. I read this post, which is similar I think, and someone advised the use of Inline model formsets. But I don't understand how to solve my problem with the latter. Could you help me?
I had no answer from Stackoverflow but a friend of mine solved my problem like this with inline formset:
# forms.py
class AssessmentForm(ModelForm):
class Meta:
model = Assessment
exclude = ('category',)
CategoryAssessmentFormSet = inlineformset_factory(
Assessment,
Assessment.category.through,
fields=['category', 'value'],
can_delete=False,
extra=Category.objects.count(),
max_num=Category.objects.count(),
widgets={'category': Select(attrs={'hidden': 'true'})}
)
in my view, to render the formset:
# views.py
initial = [{'category': category} for category in Category.objects.all()]
formset = CategoryAssessmentFormSet(initial=initial)
Select is hidden but I still want the name of the selected field, in my template:
# template
{{ formset.management_form }}
{% for form in formset %}
<div class="p-2">
{% for value,selected in form.fields.category.choices %}
{% if value == form.category.value %}{{ selected }}{% endif %}
{% endfor %}
{{ form.category }}
</div>
<div>
{{ form.value}}
</div>
{% endfor %}

Categories

Resources