Django: Querying Multiple Foreign Keys - python

First off, I'm sure this is a simple question. I'm just getting started with Django and as all beginners do, I thought I'd build a simple blog.
I have a simple data model, a Post model that contains with a link to a user via a FK.
models.py
class Post(TimeStampedActivate):
"""
A blog post
"""
title = models.CharField(max_length=255)
slug = models.SlugField()
excerpt = models.TextField(blank=True)
body = models.TextField()
publish_at = models.DateTimeField(default=datetime.datetime.now())
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=False, help_text='Is the blog post active?')
user = models.ForeignKey(User, related_name='post_user')
def __unicode__(self):
return self.title
I then want a page that lists all of the Posts alongside the username of the person who created the post. My current view looks like this:
views.py
def index(request):
posts = Post.objects.filter(active=True)
user = User.objects.get(id=1)
return render(request, 'blog/index.html', {'posts': posts, 'user': user})
My template
At present this just displays the users name that matches an ID of 1.
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt|truncatewords:30 }}</p>
<p>Posted by {{ user.first_name }} {{ user.last_name }}</p>
{% endfor %}
How would I modify my views.py file to ensure I get the first and last name of the user responsible for the post?

View:
def index(request):
posts = Post.objects.filter(active=True)
return render(request, 'blog/index.html', {'posts': posts})
Template:
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt|truncatewords:30 }}</p>
<p>Posted by {{ post.user.first_name }} {{ post.user.last_name }}</p>
{% endfor %}

Use "post.user" in your template instead of just "user".
{{ post.user.first_name }} {{ post.user.last_name }}
"user" is the current logged-in user.
If you use RequestContext & added auth to your TEMPLATE_CONTEXT_PROCESSORS, user is equal to request.user. https://docs.djangoproject.com/en/dev/topics/auth/#authentication-data-in-templates

Related

Django: Access related object attributes from a form field in a template?

I'm building a page that allows users to edit Task and related Activity records (one task can have many activities), all on the same page. Here are extracts from my code...
models.py
from django.contrib.auth.models import User
class Task(models.Model):
category = models.CharField(max_length=300)
description = models.CharField(max_length=300)
class Activity(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
notes = models.TextField(blank=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
The activity "owner" is linked to a User from the Django standard user model.
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
formset.save()
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
And manage_task.html excerpt:
<h2>{{ task.category }}</h2>
<form method="post">
{% csrf_token %}
{{ task_form.description }}
{% for form in formset %}
{{ form.id }}
{{ form.title }}</br>
{{ form.notes }}</br>
{% if user.id == form.owner.value %}
You own this Activity!</br>
{% else %}
{{ form.owner.first_name }} owns this Activity</br>
{% endif %}
{% endfor %}
<input class="save" type="submit" value="SAVE">
</form>
Perhaps obviously, {{ form.owner.first_name }} doesn't return anything. form.owner understandably renders as a select drop down with all the Users listed - it's some sort of iterable object so it doesn't have a first_name attribute.
I think I need to have the first_name field in each form in the formset so it ties up properly with the correct Activity form.
I feel like I might have to adapt the view in some way to go through all the forms in the formset and use the owner.id (if it exists) to access the related User and add the first_name attribute as an extra field in the form somehow? That doesn't feel very Djangoish, there must be a better way.
How can I use the form field form.owner to get the first_name attribute from the User object that's related to the Activity so I can use it in the template?
You should not take such value from form directly. It may be risky (i.e. changing real-life). But if you are aware of it and ok with that, try pointing to specific instance of form:
{{ form.instance.owner.first_name }}

Posts not showing author who wrote it in Django

Basically, I'm writing an app in which people can make blog and image posts. So far, I've completed users to be able to write text posts. However, when I try to create a post, it returns "By: None" when it should be returning "By: shrey". In this case, Bob is the author. Here's an image:
Here's an image for the post creation view:
Theoretically, when I enter a post it should say who it was written by.
Here's the template for the create post:
{% extends "social/base.html" %}
{% load crispy_forms_tags %}
{% block content4 %}
<h1>Make Your Post</h1>
<p>Write a post / Share an image</p>
<br>
<div class="container">
<form method="post">
{% csrf_token %}
{{form|crispy}}
<button type="submit" name="button">Make Post</button>
</form>
</div>
{% endblock content4 %}
Here's the function for the create post view:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.author = self.request.user
print(self.request.user)
return super().form_valid(form)
Thank you in advance.
EDIT: Home Page Template (template which displays the posts):
{% extends "social/base.html" %}
{% block content %}
<h1>Your Feed</h1>
<p>This is your feed. Here, you'll see posts from people you follow.</p>
{% if user.is_authenticated %}
<p>You are logged in as {{user.username}}. This is your feed.</p>
{% else %}
<p>You are not logged in. This is a random feed.</p>
{% endif %}
{% for post in posts %}
<h1>{{ post.post_title }}</h1>
<p>By {{ post.post_author }} on <i>{{ post.post_date }}</i></p>
<p>{{ post.post_text_content }}</p>
{% endfor %}
Click here to make a post.
<br>
Click here to logout.
<br>
Click here to login.
<br>
Click here to sign up and make an account.
<!--<p>Want to post something? Enter your info here: </p> -->
{% endblock content %}
Posts Model:
class Posts(models.Model):
post_title = models.CharField(max_length = 40, help_text = 'Enter post title')
post_text_content = models.TextField(max_length = 1000)
post_author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
post_date = models.DateField(auto_now = True, auto_now_add = False)
#Make optional Image Field
class Meta:
ordering = ['post_title', 'post_author', 'post_date', 'post_text_content']
def __str__(self):
return self.post_title
def get_absolute_url(self):
return reverse('social-home')
The name of the field is post_author, not author, hence you should set post_author:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.post_author = self.request.user
return super().form_valid(form)
That being said, normally in Django one does not prefixes the model fields with the name of the model. One reason not to do that is that you can define abstract models where you define the field once, and then use inheritance to add the field to other models.

How to acces the the django ManyToMany Field in django Template

I have set of attributes in my Models from which one of the attribute is of Type ManyToMany Field. I am able to access all the Attributes in Template instead one which is ManyToMany Field.
I have tried following in my template
{% for post in all_posts %}
{{ post.likes }}
{% endfor %}
models.py
class Posts(models.Model):
title = models.CharField(max_length=250, blank=False)
content = models.CharField(max_length=15000,
help_text="Write Your thought here...")
creation_time = models.DateTimeField(auto_now_add=True, editable=False)
likes = models.ManyToManyField(User, blank=True, related_name='likes')
views.py
def home(request):
template = loader.get_template('home.html')
all_posts = Posts.objects.all()
context = {
'all_posts': all_posts,
}
return HttpResponse(template.render(context, request))
When i Use {{ post.likes }} what renders on page is auth.User.None
You will have to traverse over all the likes for the selected post
Try something like this:
{% for post in all_posts %}
{% for like in post.likes.all %}
{{ like }}
{% endfor %}
{% endfor %}

Django user posts to be filtered from all posts and displayed in user profile

I am new to Python and Django and now developing a blog.I have this model that users can log in to the site and add their posts and all posts are displayed in home. At the same time I want the posts by the user to be displayed in user profile.
My model for the blogpost is
from django.db import models
from django.utils import timezone
class Blogpost(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
text = models.TextField()
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
and the views.py:
views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from blogpost.models import Blogpost
def home(request):
context = {}
template = 'home.html'
return render(request,template,context)
def about(request):
context = {}
template = 'about.html'
return render(request,template,context)
#login_required(login_url='/accounts/login/')
def userprofile(request):
user = request.user
context = {'user': user}
user_posts=Blogpost.objects.filter(author=request.user).order_by('-published_date')
template = 'profile.html'
return render(request,template,context,{'user_posts':user_posts})
I am using this template to display the posts from users returned by the query set.
profile.html
{% extends 'base.html' %}
{% load staticfiles %}
{% block blogprofile %}
{{ user.username }}
{{ user.email }}
{% for Blogpost in user_posts %}
<div class="post">
<div class="date">
<p>{{ Blogpost.published_date }}</p>
<p>{{ Blogpost.author }}</p>
</div>
<h1>{{ Blogpost.title }}</h1>
<p>{{ Blogpost.text|linebreaksbr }}</p>
</div>
{% endfor %}
{% endblock %}
Now I am getting only user name and email when I open the profile.html where as the post is not getting retrieved. Can anyone please correct me on where I am making mistake.
Change your view to this:
#login_required(login_url='/accounts/login/')
def userprofile(request):
user = request.user
user_posts = Blogpost.objects.filter(author=request.user).order_by('-published_date')
template = 'profile.html'
return render(request, template, {'user_posts':user_posts,'user': user})

Django - Find out which model instance I'm editing in an inline formset

Note: I'm new to Django and Python. I'm not sure if I've misunderstood the use of inline formsets in some way.
I needed to be able to edit certain fields for multiple instances of a Guest model (linked to a parent Invite model) at the same time in one form. After a lot of back and forth, I managed to set up an inline formset that submits successfully.
However, the interface is confusing, because you don't know which Guest you're filling in details for. Each Guest has a guest_name field, and I just want to display that name for each form, so that users know who they're editing for.
Here's my (condensed) view:
def extra_view(request, code):
# Get the specific invite
invite = get_invite(code)
# Get the context from the request.
context = RequestContext(request)
# Store guests attending object
guests_attending = invite.guest_set.filter(attending=True, invite=invite)
# Create the formset for each Guest
GuestFormset = inlineformset_factory(Invite, Guest,
form=ExtraForm,
fields=('diet', 'transport'),
extra=0,
can_delete=False)
if request.method == "POST":
formset = GuestFormset(request.POST, request.FILES,
instance=invite,
queryset=Guest.objects.filter(attending=1))
if formset.is_valid():
# Save the data to the database.
formset.save()
# Redirect stuff here
if guests_attending.count() > 0:
formset = GuestFormset(instance=invite, queryset=Guest.objects.filter(attending=1))
# Return the view
return render_to_response('app/extra.html', {
'GuestForm': formset,
'invite': invite,
'guests_attending': guests_attending,
'errors': formset.errors
}, context)
else:
# Backup for if no guests are attending
Here's how the models look:
class Invite(models.Model):
# Code to identify invites by
code = models.CharField(max_length=6, default=code_generator(4, do_check=True), unique=True)
group_name = models.CharField(max_length=200)
def __str__(self):
return self.group_name
class Guest(models.Model):
invite = models.ForeignKey(Invite, on_delete=models.CASCADE)
guest_name = models.CharField(max_length=200)
diet = models.CharField(max_length=250, null=True, blank=True)
transport = models.NullBooleanField(default=False)
attending = models.NullBooleanField(default=False)
def __str__(self):
return self.guest_name
And here's my template
{% if invite %}
<form method="post" action="">
{% csrf_token %}
{{ GuestForm.management_form }}
<table>
{% for form in GuestForm %}
<!-- Which guest am I editing for?! -->
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
<div>
{{ field.help_text }}
{{ field }}
</div>
{% endfor %}
{% endfor %}
</table>
<button type="submit" class="btn">Continue</button>
</form>
{% endif %}
You can access the form's instance with form.instance. In the template, you could do something like:
{{ form.instance.guest_name }}

Categories