List of current user objects in Django ListView - python

I want to render list of all objects on my template, for which their author is the currently logged in user. I passed the username of current user to url.py:
My List
My urls.py:
path('myscenarios/<str:username>/', MyScenarioListView.as_view(), name='myscenarios'),
My question is how to build the queryset in views.py and what to type in template block in my html?
class MyScenarioListView(LoginRequiredMixin, ListView):
model = Scenario
template_name = 'testmanager/myscenarios.html'
context_object_name = 'myscenarios'
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Scenario.objects.filter(scenarioAuthor = user).order_by('-date_posted')
What code should I type in my myscenarios.html file?

I want to render list of all objects on my template, which their author is current logged user.
Then you should not encode the user in the path, since a "hacker" can then simply change the URL to see the items belonging to a different user.
You can make use of self.request.user here. The path thus looks like:
path('myscenarios/', MyScenarioListView.as_view(), name='myscenarios'),
and in the view, we use:
class MyScenarioListView(LoginRequiredMixin, ListView):
model = Scenario
template_name = 'testmanager/myscenarios.html'
context_object_name = 'myscenarios'
def get_queryset(self):
return Scenario.objects.filter(
scenarioAuthor=self.request.user
).order_by('-date_posted')
It will pass the Scenarios as myscenarios to the template, so you can render this with:
{% for scenario in myscenarios %}
{{ scenario }}
{% endfor %}

Related

Django model CreateView does not render form fields in HTML

I am trying to setup a class based 'CreateView' for a model in my django site, but when the create html page renders, the model fields are not rendered. Only the submit button shows up on the web page. However, when debugging, I overrided the 'form_invalid' method in the class view, and the form object had the required HTML for all fields stored in the object. If I take this HTML and manually add it to the HTML of the create page in the browser I can fill out the fields and post the data to the database.
At this point I have not found an obvious answer as to why the form fields are not rendered so any help on this would be greatly appreciated.
environment used: python 3.7.3, django 2.2.3
Solution:
This issue was fixed by changing the form name in the view context data.
In views.py:
def get_context_data(self, *args, **kwargs):
context = super(CreateAlertView, self).get_context_data(**kwargs)
context["alert_form"]=context["form"]
return context
Or...
In the HTML template change 'alert_form' to 'form' to match the default context.
models.py:
class Alert(models.Model):
RAIN = 'Rain'
SNOW = 'Snow'
COLD = 'Cold'
HEAT = 'Heat'
WEATHER_CHOICES = [
(RAIN, 'Rain'),
(SNOW, 'Snow'),
(COLD, 'Cold'),
(HEAT, 'Heat'),
]
DAILY = 'Daily'
WEEKLY = 'Weekly'
INTERVAL_CHOICES = [
(DAILY, 'Daily'),
(WEEKLY, 'Weekly'),
]
weather_type = models.CharField(max_length=15, choices=WEATHER_CHOICES, default=RAIN)
interval = models.CharField(max_length=10, choices=INTERVAL_CHOICES, default=DAILY)
search_length = models.IntegerField(default=1)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
active = models.BooleanField(default=False)
views.py:
class CreateAlertView(LoginRequiredMixin, CreateView):
template_name = 'users/alert_form.html'
#model = Alert
form_class = AlertModelForm
success_url = 'users/profile/'
def form_valid(self, form):
print('validation')
form.instance.user = self.request.user
return super().form_valid(form)
def form_invalid(self, form):
print(form) # check form HTML here
return super().form_invalid(form)
forms.py:
class AlertModelForm(ModelForm):
class Meta:
model = Alert
exclude = ['user']
urls.py:
urlpatterns = [
path('alert/create/', CreateAlertView.as_view(), name='alert'),
]
html template:
<h1>create an alert</h1>
<form method="post">
{% csrf_token %}
{{ alert_form.as_p }}
{{ alert_form.non_field_errors }}
{{ field.errors }}
<button type="submit">Save changes</button>
</form>
Create page as rendered:
Create page with manually modified HTML:
The context name for the form set by the CreateView (FormMixin) is "form", your template is referencing "alert_form"
Here is a helpful website for seeing all options available in the class based views

Render 2 models on the same template using CBV

I am trying to render two differents models using a loop in my templates/dashboard/home.html using Class Based View.
Let's have a look on my code :
views.py
class DashboardListView(ListView):
model = Links
template_name = 'dashboard/home.html'
class ContentListView(ListView):
model = Dashboard
template_name = 'dashboard/home.html'
First I would like to used the same ListView but was unable to do so.
My home.html
{% for dashboard in object_list %} {{dashboard.content}} {% endfor %}
{% for links in object_list %} {{links.content}} {% endfor %}
I would like to render those two models but I can only render one and the other object list will take the content for the preivous one.
Thanks for your help
First you can specify a different Mae for the list of your objects using:
context_object_name =‘links_list’
Then you can add different elements to the context extending this method:
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet
context[‘dashboard_list’]= Dashboard.objects.all()
return context

Displaying all the posts of user in Django

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 %}

How to search on a Many to Many field in Django?

I have a Profile model with a ManyToManyField on another model Specialty.
I want to have a simple search on the Profile model against specialties and return matching profiles. As it stands, my form displays in my template correctly, but I can't get anything after the submission.
models.py:
from django.db import models
from django.conf import settings
class Specialty(models.Model):
title = models.CharField(max_length=255)
class Meta:
verbose_name_plural = 'Specialties'
def __unicode__(self):
return u"%s" % self.title
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
specialties = models.ManyToManyField(Specialty, blank=True)
def __unicode__(self):
return u"%s" % (self.user.username)
def get_absolute_url(self):
return reverse("profile_detail", args=[str(self.user.username)])
forms.py:
from django import forms
from .profiles.models import Profile, Specialty
class ProfileSearchForm(forms.ModelForm):
specialty = forms.ModelMultipleChoiceField(queryset=Specialty.objects.all(), widget=forms.CheckboxSelectMultiple, required=False)
class Meta:
model = Profile
fields = ('specialty',)
views.py:
from django.views.generic.edit import FormView
from django.core.urlresolvers import reverse_lazy
from .forms import ProfileSearchForm
from .profiles.models import Profile
class IndexView(FormView):
template_name = 'index.html'
form_class = ProfileSearchForm
success_url = reverse_lazy('index')
def form_valid(self, form):
specialty = form.cleaned_data['specialty']
self.profile_list = Profile.objects.filter(specialty__in=specialty)
return super(IndexView, self).form_valid(form)
index.html:
<form action="{% url 'index' %}" method="get">
{{ form.as_p }}
<p><input type="submit" value="Search"></p>
</form>
<ul>
{% for profile in profile_list %}
<li>{{ profile.user.get_full_name }}</li>
{% endfor %}
</ul>
I have a feeling it has to do with self.profile_list. I don't know if/how it should go into a get_extra_context. It can't exist on the first visit, so I don't know how to make it exist or pass it around. I'm also not sure if the Profile.objects.filter(specialty__in=specialty) is quite the right way to field lookup on a many-to-many field.
I'm also open to other search suggestions like Haystack if they have advantages. I prefer a group of checkboxes, which I don't think Haystack can handle via faceting.
Thanks, Gergo and Cameron. I got it fixed now. You were right about that one problem, but there were quite a few steps left to go.
What I really wanted was a ListView plus the ability to do a simple search, which should be a FormMixin that lets me add form_class and success_url, instead of it all as a FormView.
When a default model is specified in a ListView, the view blows away the context, so form never reaches the template. get_context_data needs to add the form back to the context, of which the docs have an example.
form_valid should be removed because a search is never a POST request, despite what the docs say under the "Note" in FormMixin requiring form_valid and form_invalid.
I need get_queryset to either get a default queryset via model or read the GET request's specialties value and filter the results appropriately.
For bonus points, get_form_kwargs needs to pass the current request to the form so initial form values can remain after a page refresh. The tricky part is that when using ModelMultipleChoiceField, you have to use request.GET's getlist and not get method to read that list of values.
All together now...
forms.py:
from django import forms
from .profiles.models import Profile, Specialty
class ProfileSearchForm(forms.ModelForm):
specialties = forms.ModelMultipleChoiceField(queryset=Specialty.objects.all(), widget=forms.CheckboxSelectMultiple, required=False)
class Meta:
model = Profile
fields = ('specialties',)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super(ProfileSearchForm, self).__init__(*args, **kwargs)
self.fields['specialties'].initial = self.request.GET.getlist('specialties')
views.py:
from django.views.generic import ListView
from django.views.generic.edit import FormMixin
from django.core.urlresolvers import reverse_lazy
from .profiles.models import Profile
from .forms import ProfileSearchForm
class IndexView(FormMixin, ListView):
model = Profile
template_name = 'index.html'
form_class = ProfileSearchForm
success_url = reverse_lazy('index')
def get_queryset(self):
queryset = super(IndexView, self).get_queryset()
specialties = self.request.GET.getlist('specialties')
if specialties:
queryset = queryset.filter(specialties__in=specialties).distinct('user')
return queryset
def get_form_kwargs(self):
kwargs = super(IndexView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
form_class = self.get_form_class()
context['form'] = self.get_form(form_class)
return context
I think you're looking for Profile.objects.filter(specialties__in=specialty) - profile doesn't have a specialty field, it has a specialties field.

Django's get_context_data function not working?

I'm practicing with Django's Class Based View.
It seems like my overridden get_context_data function is not working properly, but I have no idea what is wrong :(
My code:
urls.py
url(r'^user/(?P<pk>\d+)/posts/$', UserPosts.as_view(), name='user_post'),
views.py
class UserPosts(ListView):
template_name = 'app_blog/user_posts_page.html'
context_object_name = 'post_list'
def get_queryset(self):
self.user = get_object_or_404(User, id=self.kwargs['pk'])
return self.user.post_set.order_by('-id')
def get_context_data(self, **kwargs):
context = super(UserPosts, self).get_context_data(**kwargs)
context['user'] = self.user
return context
user_post_page.html
{% block content %}
<div class="main_content">
<h2>Welcome to {{user.username}}'s User Post Page!</he>
<ul>
{% for post in post_list %}
<li>{{post.post_title}}</li>
{% endfor %}
</ul>
</div>
{% endblock %}
The html page correctly displays the user's post_list, BUT the h2 tag displays:
Welcome to 's User Post Page!
I'm pretty sure I passed the 'user' variable in the get_context_data function, but the html page does not displa the user.username... Any idea why this is happening :(??
Thanks
Use another name that is not user. It seems like RequestContext overwrite user variable.
Please see the default TEMPLATE_CONTEXT_PROCESSORS, which set the django.contrib.auth.context_processors.auth.
If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every RequestContext will contain these variables:
user – An auth.User instance representing the currently logged-in user (or an AnonymousUser instance, if the client isn’t logged in).
perms – An instance of django.contrib.auth.context_processors.PermWrapper, representing the permissions that the currently logged-in user has.
So you'd better give your user variable another name.
As the other answers said, don't use the variable name user. But even more importantly, in this particular case the ListView is not the best generic class to use; instead, you're better off using the DetailView, which makes your view much simpler:
class UserPosts(DetailView):
model = User
template_name = 'app_blog/user_posts_page.html'
context_object_name = 'listed_user'
The only change in your template is to use listed_user instead of the user variable. Since DetailView already looks into the pk URL argument, it will select and return the right User automatically.

Categories

Resources