How to pass a custom model manager queryset to a template - django

I am trying to pass a custom query to a template but the query results are not being displayed.
I had a working solution for this but I wanted to implement a custom model manager to simplify the code but I cant get the final step work - that is displaying the results on the template
I have a custom manager:
from django.db import models
class ProfileQuerySet(models.QuerySet):
def get_users_follows(self, user):
print(user)
return self.filter(followed_by__user__username=user)
class ProfileManager(models.Manager):
def get_queryset(self):
return ProfileQuerySet(self.model, using=self._db)
def get_users_follows(self, user):
return self.get_queryset().get_users_follows(user)
which is instantiated in the model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
follows = models.ManyToManyField('self', related_name='followed_by', symmetrical=False)
objects = ProfileManager()
my view is as follows:
class FollowsListView(LoginRequiredMixin, ListView):
# Follow.follow_data = [[],[]]
model = Profile
template_name = 'blog/follows.html' # <app>/<model>_<viewtype>.html
# paginate_by = 6
def get_queryset(self):
follow_data = Profile.objects.get_users_follows(self.request.user)
context = {'follow_data', follow_data}
return context
In the template i am calling the follow_data like so:
{{ follow_data }}
{% for p in follow_data %}
{% if p.user %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ p.user.profile.image.url }}" alt="">
{{ p.user.profile.user }}
{% else %}
<p>You are not following any users</p>
{% endif %}
</article>
{% endfor %}
but no results are being displayed so I am not quite sure what I am doing wrong here. Can anyone provide me weith a quick pointer on what ive done wrong?

There is a typo:
context = {'follow_data', follow_data}
You've created a set, but what you really want is a dict
context = {'follow_data': follow_data}
edit:
but you shouldn't be returning a context here anyway, you should return a queryset. simply do return follow_data should work.
in your template, you can refer to the queryset with {% for user in object_list %}
If you don't want to call it "object_list" you'll need to override get_context_data
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["follow_data"] = context.pop("object_list")
return context
doc: https://docs.djangoproject.com/en/2.2/ref/class-based-views/generic-display/#listview

Related

Making a CreateForm with choices based on database values

I am making a django project and I have a form for the User to add a Vehicle Manually that will be assigned to him. I also would like to had an option for the user to choose a vehicle based on the entries already present in the database.
vehicles/models.py
class Vehicle(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
nickname = models.CharField(unique = True, max_length=150)
date_joined = models.DateTimeField(default=timezone.now)
brand = models.CharField(max_length=150)
battery = models.CharField(max_length=150)
model = models.CharField(max_length=150)
def __str__(self):
return self.nickname
def get_absolute_url(self):
return reverse('vehicle-list')
class Meta:
db_table = "vehicles"
I created a form so the user can add his Vehicles as such:
vehicles/forms.py
class VehicleAddFormManual(forms.ModelForm):
class Meta:
model = Vehicle
fields = ('brand','model', 'battery', 'nickname')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
self.fields['brand']
self.fields['model']
self.fields['battery']
self.fields['nickname']
The corresponding view:
vehicles/views.py
class AddVehicleViewManual(LoginRequiredMixin, CreateView):
model = Vehicle
form_class = VehicleAddFormManual
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
The html file:
vehicles/templates/vehicles/vehicle_form.html
{% extends "blog/base.html" %}
{% block content %}
{% load crispy_forms_tags %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Vehicle</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Submit</button>
</div>
</form>
</div>
{% endblock content %}
I would like to add another form in which the user has a dropdown with option with the brands, models and batteries that already exist in the database. If there's a car in the database with brand: Tesla, model: Model 3, battery: 50 kWh, then it would appear in the dropbox as a choice for each field.
I'm not sure how to do this and sorry for the newbie question... Thanks in advance!
I once had to do something similar, but I needed a form which had one checkbox for each item in a list of externally-supplied strings. I don't know if this is the cleanest way, but I used python metaclasses:
class SockSelectForm(forms.Form):
#staticmethod
def build(sock_names):
fields = {'sock_%s' % urllib.parse.quote(name):
forms.BooleanField(label=name, required=False)
for name in sock_names}
sub_class = type('DynamicSockSelectForm', (SockSelectForm,), fields)
return sub_class()
In my get() method, I instantiate it as:
form = SockSelectForm.build(names)
and the corresponding form handling in the post() method is:
form = SockSelectForm(request.POST)
I suspect if you look under the covers of Django's ModelForm, you'd see something similar, but I couldn't use ModelForm because it's too closely tied to the model system for what I needed to do.
model.py
class DropdownModel(models.Model):
brand = models.CharField(max_length=150)
battery = models.CharField(max_length=150)
model = models.CharField(max_length=150)
def __str__(self):
return self.brand.
form.py
from .models import DropdownModel
all_brand = DropdownModel.objects.values_list('brand','brand')
all_battery = DropdownModel.objects.values_list('battery','battery')
all_model= DropdownModel.objects.values_list('model','model')
class DropdownForm(forms.ModelForm):
class Meta:
model = DropdownModel
fields = "__all__"
widgets = {
'brand':forms.Select(choices=all_brand),
'battery':forms.Select(choices=all_battery),
'model':forms.Select(choices=all_model),
}
view.py
from django.shortcuts import render
from .form import DropdownForm
# Create your views here.
def HomeView(request):
form = DropdownForm()
context = {'form':form}
return render(request,'index.html',context)
index.html
{% extends "base.html" %}
{% load static %}
{% block title %}
Index | Page
{% endblock title %}
{% block body %}
{{form.as_p}}
{% endblock body %}
Output-
Note- if u can't see updated values in dropdown do server restart because localhost not suport auto update value fill in dropdown it's supoorted on live server
Thank you

Django CBV inheritance not working as expected

I use these two classes to get the context data and check permissions respectively:
class RestaurantView(View):
def get_context_data(self):
ctx = {'restaurant': get_object_or_404(Restaurant, slug=self.kwargs['slug'])}
return ctx
class ManageRestaurantMixin(LoginRequiredMixin, UserPassesTestMixin, RestaurantView):
def test_func(self):
ctx = super().get_context_data()
return ctx['restaurant'].owner == self.request.user
Now the first one is working, so when i don't need permission i get the expected behavior, for example with this DetailView:
class Info(RestaurantView, DetailView):
model = Restaurant
context_object_name = 'restaurant'
template_name = 'restaurant/info.html'
But then if inherit from ManageRestaurantMixin the permissions are checked as expected, but the context object is not working and the template displays an empty form:
class EditInfo(ManageRestaurantMixin, UpdateView):
model = Restaurant
context_object_name = 'restaurant'
template_name = 'restaurant/edit_info.html'
fields = ['name', 'address', 'logo']
success_url = '/account/myrestaurants'
I get that the context gets overwritten, but i don't get how.
How can i solve this?
Is there a better way to handle this kind of situations?
Edit:
edit_info.html:
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<input class="btn btn-primary" type="submit" value="update">
</form>
{% endblock %}

Passing two Models in update view to a same template

I want to render two models User (built in model) and Profile model to the same template profile_form.html so that the user can update the data of both User model as well as Profile model
This is my Profile model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.png', upload_to='profile_pics')
description = models.TextField()
def __str__(self):
return self.user.username + "'s Profile"
This is my profile_form.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block title %}
Make your Profile
{% endblock title %}
{% block content %}
<div class="container mb-6">
<form action="" method="POST" class="form-group">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>
{% endblock content %}
This is my UserUpdateView
class UserUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model=User
fields=['username', 'first_name', 'last_name']
success_url='/'
def test_func(self):
x = self.request.user.id
y = self.kwargs['pk']
if x == y:
return True
else:
if self.request.user.is_authenticated:
raise Http404("You are not authenticated to edit this profile")
I want my Profile model's to be below User model's form
Please help me with this
To add a OneToOne-relation into the same view, you just need to overwrite the get_context_data method and provide an additional form.
If you don't have a profile form yet, just create a simple one:
#yourapp/forms.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = "__all__"
Now, to use this in your updateview, import it and you will need to change it like this:
class UserUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model=User
fields=['username', 'first_name', 'last_name']
success_url='/'
# create context manually
def get_context_data(self, **kwargs):
data = super(UserUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
data["profile"] = ProfileForm(self.request.POST)
else:
# accessing the profile object
data["profile"] = ProfileForm(instance=self.object.profile)
return data
And now your template will have access to the context profile
...
{{ form|crispy }}
{{ profile|crispy }}
...

ValueError at /profile/:The 'image' attribute has no file associated with it

When a user registers for my app.I receive this error when he reaches the profile page
'ValueError at /profile/:The 'image' attribute has no file associated with
it.'
This is my profile model:
class Profile(models.Model):
Full_Name = models.CharField(max_length=32,blank=True)
Name = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
E_mail = models.EmailField(max_length=70,blank=True)
Qualification = models.CharField(max_length=32,blank=True)
Permanant_Address = models.TextField(blank=True)
image = models.ImageField(upload_to='user_images', null=True, blank=True)
def __str__(self):
return str(self.Name)
def get_absolute_url(self):
return reverse("userprofile:profiledetail")
#property
def image_url(self):
if self.image and hasattr(self.image, 'url'):
return self.image_url
This is my form:
class profileform(forms.ModelForm)"
class Meta:
model = Profile
fields = ('Full_Name','Name', 'E_mail','Qualification','Permanant_Address','image')
This is my view:
from django.shortcuts import render
from django.views.generic import DetailView,UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from userprofile.models import Profile
from userprofile.forms import profileform
# Create your views here.
class profiledetailview(LoginRequiredMixin,DetailView):
context_object_name = 'profile_details'
model = Profile
template_name = 'userprofile/profile.html'
def get_object(self):
return self.request.user.profile
class profileupdateview(LoginRequiredMixin,UpdateView):
model = Profile
form_class = profileform
template_name = 'userprofile/profile_form.html'
def get_object(self):
return self.request.user.profile
And in my template I have done something like this:
<img class="profile-user-img img-responsive img-circle" src="{{
profile_details.image.url|default_if_none:'#' }}/" alt="User profile
picture">
I'm been reading the documentation for Built-in template tags and filters I think a solution here is to use and I think I can't seem to use template tag properly.
How can I configure this template to make picture an option. If their are no picture leave it but display the persons name.
Thank you
You are trying to get the url for an image that doesn't exists.
Basically if you're trying to check if an image exists, then you have to do it this way:
if profile_details.image:
url = profile_details.image.url
or in your case:
src={% if profile_details.image %}{{ profile_details.image.url }}{% else %}#{% endif %}
My dear friend, Navid's solving is good but not enough because If user hasn't profile picture you should show default image easily (not need migration). So you can follow below steps:
Add this method to your person model:
#property
def get_photo_url(self):
if self.photo and hasattr(self.photo, 'url'):
return self.photo.url
else:
return "/static/images/user.jpg"
You can use any path (/media, /static etc.) but don't forget putting default user photo as user.jpg to your path.
And change your code in template like below:
<img src="{{ profile.get_photo_url }}" class="img-responsive thumbnail " alt="img">
If your error comes from the HTML and not the admin site, then do like this.
{% if profile_details.image %} # make sure that it's not .url in the end
<img src="{{ profile_details.image.url }}" alt="{{ profile_details.title }}">
{% else %}
<p>No Image</p>
{% endif %}
Change the profile_details, if your context is named differently,
Example
context = {
'products': products,
}
also change image, if your ImageField is named differently
Example:
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True, )
<!-- For me it looked like this -->
{% for product in products %}
{% if product.thumbnail %}
<img src="{{ product.thumbnail.url }}" alt="{{ product.title }}">
{% else %}
<p>No Image</p>
{% endif %}
{% endfor %}
very easy! this was what i did: i deleted all my old post that doesn't have an image attached to it, made a new one and it worked fine.

NoReverseMatch at /category/clothes/ Django class based views

I have a django project that is close to an e-commerce wesite in terms of functionality.
There are four pages linked to one another. First page displays Categories, 2nd Subcategories, 3rd Product list and 4th Product detail and I'm using slugs to navigate.
ERROR
Reverse for 'product-list' with arguments '('', 'women-clothes')' and keyword arguments '{}' not found. 1 pattern(s) tried: ['category/(?P<category_slug>[-\\w]+)/(?P<subcategory_slug>[-\\w]+)/$']
Category to Subcategory linking code on the category_list.html is {{ category.name }} and on the views.py
class CategoryListView(ListView):
models = Category
template_name = 'products/category_list.html'
context_object_name = "Category list"
def get_queryset(self):
"""
Returns all categories.
"""
return Category.objects.get_queryset().all()
and urls.py
app_name = 'products'
urlpatterns = [
url(r'^$', CategoryListView.as_view(), name='categories'),
url(r'^(?P<category_slug>[-\w]+)/$', SubcategoryListView.as_view(), name='sub-category'),
url(r'^(?P<category_slug>[-\w]+)/(?P<subcategory_slug>[-\w]+)/$', ProductListView.as_view(), name='product-list'),
url(r'^(?P<category_slug>[-\w]+)/(?P<subcategory_slug>[-\w]+)/(?P<pk>\d+)/$', ProductDetailView.as_view(), name='product-detail'),]
The problem is linking subcategory_list.html to product_list. Since I need a category_slug and subcategory_slug to be pass to
{{ object.name }}.
I don't know how to implement this logic to using cbv. I want to pass category_slug since it is from a Category model and querying from Subcategory model.
views.py
class SubcategoryListView(ListView):
"""
Browse all products in the sub-catalogue.
"""
model = Subcategory
template_name = 'products/subcategory_list.html'
context_object_name = "Sub-Category list"
category_model = Category
def get_queryset(self):
"""
Returns all sub-categories.
"""
self.category = get_object_or_404(Category, category_slug = self.kwargs.get('category_slug'))
return Subcategory.objects.filter(category = self.category)
category.html which works.
{% for category in object_list %}
<div class="col-xs-12 col-md-12">
{{ category.name }}
<p>{{ category.category_slug }}</p>
</div>
{% endfor %}
subcategory.html
{% for object in object_list %}
<div class="col-xs-12 col-md-12">
{{ object.name }}
<p>subcategory_slug:{{ object.subcategory_slug }}</p>
</div>
{% endfor %}
How can get category_slug and pass it in the above view so as I can iterate on them on the template?
I don't really see what this has to do with CBVs. You haven't shown much of your template, but presumably you are iterating over subcategories and want to link to the individual list page for that subcategory. So, you just need to pass the slug and the category slug for the current subcategory in your loop.
This would be easier if you showed the rest of your template and your models, but assuming object is the subcategory, with a field called "subcategory_slug`, and the SubCategory model has an FK to Category:
{{ object.name }}
I was able to solve this error.
Changes
views.py
class SubcategoryListView(ListView):
"""
Browse all products in the sub-catalogue.
"""
model = Subcategory
template_name = 'products/subcategory_list.html'
context_object_name = "Sub-Category list"
category_model = Category
def get_queryset(self):
"""
Returns all sub-categories.
"""
self.category = get_object_or_404(Category, category_slug = self.kwargs.get('category_slug'))
return Subcategory.objects.filter(category = self.category)
def get_context_data(self, **kwargs):
"""
Returns self.category_slug needed
on the subcategory_list.html as a
one of the {% url %} slug params.
"""
context = super(SubcategoryListView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['category_slug'] = self.kwargs.get('category_slug')
return context
On the subcategory_list.html i changed object.category_slug to category_slug.
subcategory_list.html
{{ object.name }} .