I am struggling to reference my ForeignKey from a basic DetailView in Django.
The models.py I am using:
class Posts(models.model):
url = models.URLField()
class Comment(models.model):
post = models.ForeignKey(Posts, related_name='comments', on_delete=models.CASCADE)
content = models.CharField(max_length=500, blank=False)
views.py:
class PostDetailView(DetailView):
model = Posts
context_object_name = 'posts'
I am trying to reference the comments in my posts detail page.
posts_details.html:
{% for comment in posts.comments.all %}
{{comment.content}}
{% endfor %}
I have also tried changing posts.comments.all to posts.comments_set.all and still am getting no results.
I feel like it is something small that I am missing, but I can't figure it out.
The data is there, and it was input correctly with the foreign key reference, but I cannot reference it through the detail view.
Edit with answer:
I was able to get this to work fairly simply by adding this to the comment model:
def get_absolute-url(self):
return reverse('post_detail', kwargs={'pk': self.post.pk})
That allowed me to access the post in the post_detail.html with the following loop:
{% for comment in posts.comments.all %}
{{comment.content}}
{% endfor %}
class Posts(models.model):
url = models.URLField()
def get_all_comments(self):
return Comment.objects.filter(post=self.pk)
Use this method, add the returned queryset to the context.
Your Posts model has no comments field ... so posts.comments.all is always empty. Unfortunately you do not get an error message if you try to access non existing fields in template tag
Related
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 %}
I am new to django but really want to learn how to use this framework.
What want to do :
I have a form, that allows the user to create a new client for example.
So in this form I have 3 fields :
Name
Description
Skills
The 'Skills' field is currently a text area, where the user would copy and paste a list already existing in another document(excel). Therefore, each element is separated by a splitline.
What I would like to do is :
create in the database the client entered by the user
In the database, link the description entered by the user to the client
In the database, link each skill entered by the user to the name so that I can work on them independantly of each other in further functions/apps. I don't want all of the skills to be linked to the client as one big bloc.
So I read the documentation and thought about using a ForeignKey. The thing is I was unable to get an input text area when using a ForeignKey. Everytime I used it I got an empty 'select list' like this ...
Empty list field
And even though I would be able to get that, I still don't know how to treat each element separatly of the others..
This is the code I have now :
model.py
class Post(models.Model):
name = models.CharField(max_length=100, null=True)
description = models.TextField(null=True)
skills = models.TextField(null=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['name', 'description', 'skills']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
post_form.html
{% extends "taskflow/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Client</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Save</button>
</div>
</form>
</div>
{% endblock content %}
As an example of what I tried to implement using a Foreign Key :
models.py
class Skills(models.Model):
element = models.TextField(null=True)
class Post(models.Model):
name = models.CharField(max_length=100, null=True)
description = models.TextField(null=True)
skills = models.ForeignKey(Skills, on_delete=models.CASCADE)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['name', 'description', 'skills']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
If in views.py, instead of calling 'skills' I call 'element', I get an error saying 'element' is undefined.
I thought that using a ForeignKey would include the 'element' field contained in Skills in the 'skills' field contained in Post. So from what I understand it is not the case.
What am I doing wrong?
Any information and tips would be welcome.
Thanks a lot.
Displaying foreign key field
If you want to display skills.element in your view then you don't have to call skills.element in fields just define __str__() method in your Skills model.
class Skills(models.Model):
element = models.TextField(null=True)
def __str__(self):
return self.element
p.s. Regarding the relation between Post and Skill, I think it might be Many to Many because one Post can have many Skills and one Skill can belong to many Posts but your current model doesn't allow that (Any Post can have only one Skill while one Skill belong to many Post).
You can find out more about how to implement many to many relationship in Django at https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/
** I CHANGED SKILLS TO ADDRESS **
I thought about this :
Get a TextField instead of ForeignKey
the user enters his list like this for example :
TextField
Then in Views.py I try to parse it, by creating a new field and use splitlines() like this:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['name', 'description', 'address']
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.address_list = self.object.address.splitlines()
form.instance.author = self.request.user
self.object.save()
return super().form_valid(form)
Here I think i need to do something like
for element in self.object.address_list:
#create object here
But I don't really know how to do it and most of the time I get
Post object has no attribute object
And also, in my template if I use for example :
<p class="article-content">{{ post.address_list }}</p>
I get ['123456', '123457', '123458']
And if I use :
{{ post.address_list.1 }}
I'll get the first str character which is '
So the general idea would be to get from this form this output in the db :
db
as I want to create an instance of each address in the db, linked to the client.
Thanks for your very appreciated help !
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
I want to get all posts that a user send. For example user: admin, it will show admin's posts. I write some code, but probably I made a mistake and I got an error.
The error that I get:
AttributeError at /index_page/
'WSGIRequest' object has no attribute 'models'
Here is my code:
views.py
def index_page(request):
logged_in_user = request.models.author
logged_in_user_posts = Post.objects.filter(author=user)
return render(request, 'blog/post_list.html', {'posts': logged_in_user_posts})
models.py
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = RichTextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
post_list.html
<div>
{% for post in posts %}
Username: {{ post.author.username }}
Post: {{ post.text }}
<br>
{% endfor %}
Where is my mistake?
The error already hints to the line:
logged_in_user = request.models.author
A request object has no models attribute. It has a .user attribute that specifies the logged in user, so you can use:
logged_in_user = request.user
There is another error: you use Post.objects.filter(user=user), but there is no user variable either, there is a logged_in_user variable. So you can fix the view with:
def index_page(request):
logged_in_user_posts = Post.objects.filter(author=request.user)
return render(request, 'blog/post_list.html', {'posts': logged_in_user_posts})
Extra notes:
since you need a user, it makes sense to decorate the function with the login_required decorator [Django-doc]; and
Since Django allows you to change the User model, it might be beneficial to use the standard way to refer to the user model [Django-doc]. If you later change your mind and implement another user model, the creating migrations will automatically let the relations refer to the new model.
The two comments above are not strictly necessary to let it work. But it is what I think are good practices when developing Django apps (although of course these can be a bit "opinion-based").
The issue is in your view, request object does not contain your models.The view should be like below,
def index_page(request):
logged_in_user = request.user
logged_in_user_posts = Post.objects.filter(author=logged_in_user)
return render(request, 'blog/post_list.html', {'posts': logged_in_user_posts})
I am answering a little bit late , But may be it help someone later . you can use class-based views . In your case
views.py :
class UserPostListView(ListView):
model = Post
template_name = 'app_name/template_name.html'
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User,username=self.kwargs.get('username'))
return Post.objects.filter(author=user)
and in your urls.py :
path('user/<str:username>', views.UserPostListView.as_view(), name='user-posts'),
and then in your template :
{% block content%}
<h1> My files </h1>
{% for post in posts %}
{% endfor %}
{% endblock %}
I have created a post model and would like to view the posts in post_list. While creating the new post, it is redirecting to post_list but not displaying any post.
Also, in my post_form I have rendered the fields manually by using django templates. I couldnt figure out where I have made the mistake. Can someone please help me out. Thanks
models.py
class Post(models.Model):
author = models.ForeignKey(User, on_delete = models.CASCADE)
slug = models.SlugField(unique=True, blank=True, default=uuid.uuid1)
By default the context_object_name is object_list
Either you access your Posts list in template with object_list
{% for post in object_list %}
{{ post }} <!-- with lowercase -->
{% endfor %}
Or you change the context_object_name to post_list, so that way you will be able to access the post list with post_list in template
class PostListView(ListView):
model = Post
context_object_name = 'post_list'
You probably need to use lowercase variable name in your post_list.html.
For instance, {{ Post.title }} should probably be lowercase {{ post.title }}.
There are a few places to change that.