I have two models with a foreign key, one to many relationship so that I can get a repeater model (images) in the admin. The image repeater works fine, my problem is that the images for the field - video_stills saved on one of the film post/objects repeats across all film posts.
Here is my code:
model.py
from __future__ import unicode_literals
from django.db import models
from embed_video.fields import EmbedVideoField
from sorl.thumbnail import ImageField
class Timestamp(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
# Film Model
class Film(Timestamp):
title = models.CharField(max_length=255)
order = models.PositiveIntegerField(default=0, blank=False, null=False)
meta_description = models.TextField('Meta Description', max_length=170,
help_text='Content for description meta tag - Max 170 Characters')
slug = models.SlugField(unique=True)
image = models.ImageField(upload_to='thumb')
video = EmbedVideoField(blank=True)
director = models.CharField(max_length=255,blank=True)
cinematographer = models.CharField(max_length=255,blank=True)
producer = models.CharField(max_length=255,blank=True)
publish = models.BooleanField(default=False)
date_published = models.DateTimeField()
# thumb for admin
def image_thumb(self):
return '<img src="%s" height="200" width="300"/>' % (self.image.url)
image_thumb.short_description = 'Image'
image_thumb.allow_tags = True
# override the admin name + add ordering
class Meta(object):
ordering = ('order',)
verbose_name_plural = "Film Projects"
def __unicode__(self):
return self.title
# helper method
def get_absolute_url(self):
return "/film/%s/" % self.slug
def save(self, *args, **kwargs):
super(Film, self).save(*args, **kwargs)
# Film Stills Image Model
class FilmStillsImage(models.Model):
video_stills = models.FileField(upload_to = 'images')
film = models.ForeignKey(Film)
class Meta(object):
verbose_name_plural = "Film Stills"
views.py
# film single
def film_detail(request, slug):
film = get_object_or_404(Film, slug=slug)
# get all repeater images
film_grabs = FilmStillsImage.objects.all()
try:
next_film = film.get_next_by_date_published()
except Film.DoesNotExist:
next_film = None
try:
previous_film = film.get_previous_by_date_published()
except Film.DoesNotExist:
previous_film = None
return render(request, 'film/film_detail.html', {
'film': film,
'film_grabs': film_grabs,
'next_film': next_film,
'previous_film': previous_film
})
film_detail.html
<div class="section project-stills">
{% if film_grabs %}
<div class="row">
{% for filmstillsimage in film_grabs %}
<div class="grid-item four">
{% thumbnail filmstillsimage.video_stills "600x338" crop="center" as im %}
<img src="{{ im.url }}">
{% endthumbnail %}
</div>
{% if forloop.counter|divisibleby:4 %}
</div>
<div class="row">
{% endif %}
{% endfor %}
</div>
{% else %}
<p>Nothing found.</p>
{% endif %}
</div>
admin.py
class FilmStillsImageAdmin(admin.ModelAdmin):
pass
class FilmStillsImageInline(admin.StackedInline):
model = FilmStillsImage
max_num = 8
extra = 0
class FilmAdmin(SortableAdminMixin, admin.ModelAdmin):
model = Film
list_display = ('title', 'meta_description', 'image_thumb', 'video', 'director', 'cinematographer', 'producer', )
inlines = [FilmStillsImageInline]
prepopulated_fields = {'slug': ('title',)}
I'm sure its a simple fix, I have a feeling its because of objects.all() in my views, populating it to each item.
Apologies if its a stupid question and thanks for any help!!
You're passing all film grabs to the detail template, so naturally they'll all display. Instead of doing that, you should just follow the reverse relationship in the template to show the ones that are actually related:
FilmStillsImage
<div class="section project-stills">
<div class="row">
{% for filmstillsimage in film.filmstillsimage_set.all %}
....
and there is no need to pass film_grabs from the view at all.
Related
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
Hello guys so I am trying to add a category system for posts by following a tutorial on this website https://djangopy.org/how-to/how-to-implement-categories-in-django/ (I changed my code up a little)
I get this error 'NoneType' object has no attribute 'threads' every time I try and go to the category page to view the posts inside that category.
models.py:
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
class Category(models.Model):
name = models.CharField(max_length=100)
short_desc = models.CharField(max_length=160)
slug = models.SlugField()
parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name_plural = "Categories"
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
def save(self, *args, **kwargs):
value = self.name
self.slug = slugify(value, allow_unicode=True)
super().save(*args, **kwargs)
class Thread(models.Model):
...
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE, related_name='threads')
...
def get_cat_list(self):
k = self.category
breadcrumb = ["dummy"]
while k is not None:
breadcrumb.append(k.slug)
k = k.parent
for i in range(len(breadcrumb)-1):
breadcrumb[i] = '/'.join(breadcrumb[-1:i-1:-1])
return breadcrumb[-1:0:-1]
...
views.py:
from django.shortcuts import render, HttpResponseRedirect
from django.contrib import messages
from .models import Category, Thread
from .forms import SubmitScamNumber
def show_category_view(request, hierarchy=None):
category_slug = hierarchy.split('/')
category_queryset = list(Category.objects.all())
all_slugs = [ x.slug for x in category_queryset ]
parent = None
for slug in category_slug:
if slug in all_slugs:
#parent = get_object_or_404(Category, slug=slug, parent=parent)
parent = Category.objects.filter(slug__in=category_slug, parent=None).first()
context = {
'category': parent,
'threads': parent.threads.all(),
'sub_categories': parent.children.all(),
}
return render(request, "categories.html", context)
...
categories.html:
{% extends 'base.html' %}
{% block content %}
{% if sub_categories %}
<h3>Sub Categories</h3>
{% for i in sub_categories %}
{{ i.name }}
{% endfor %}
{% endif %}
{% if threads %}
{% for i in threads %}
<div class="columns">
<div class=" card-article-hover card">
<a href="{{ i.slug }}">
<img src="{{ i.cover_photo.url }}">
</a>
<div class="card-section">
<a href="{{ i.slug }}">
<h6 class="article-title">{{ i.title | truncatechars:30}}</h6>
</a>
</div>
<div class="card-divider flex-container align-middle">
{{ i.user.get_full_name }}
</div>
<div class="hover-border">
</div>
</div>
</div>
{% endfor %}
{% endif %}
{% endblock content %}
In show_category_view you set parent = None. Then, if there are any slugs derived from the hierarchy, you loop over them and re-assign parent to be an instance of Category. However, if there are no slugs in category_slug or the slug is not found in all_slugs or the line Category.objects.filter(slug__in=category_slug, parent=None).first() doesn't return an instance of Category, then the value of parent will remain as None and then when you try to access the threads attribute on an object which is None, as you do here...
parent.threads.all() # parent = None
...the exception you are seeing will occur.
So, exactly as the error tells you, 'NoneType' object has no attribute 'threads', because parent is still None as originally defined.
There's another null checking error lurking in this function.
def show_category_view(request, hierarchy=None):
category_slug = hierarchy.split('/')
You set the default value of the hierarchy argument to be None, then you call split on it, even though that value might be None. If you call split on None it will throw a similar AttributeError.
You also don't need to cast list over category_queryset = list(Category.objects.all()). Querysets are already list like objects that can be looped over, implement the iterator protocol, etc. (in fact, by doing this you throw away all the benefits that the queryset interface provides for you.) Here you can just do:
all_slugs = [x.slug for x in Category.objects.all()]
Sorry for this title, I wasn't sure how to name it properly. I'm having problem with getting queryset of ManyToManyField that is in relation with other ManyToManyField. So it's look like this, there is model Company that has ManyToManyField with Person and Person model got ManyToManyField with Position, because logic behind it is that 1 company can have many persons and 1 person can have few positions and can be employed by few companies (which is clear I think). I'm getting the queryset for Person in Company by this way
{% for team in brand.team.all %}
<p>{{ team.first_name }} {{ team.last_name }}</p>
<img class="img-thumbnail" src="/media/{{ team.photo }}">
<p>{{ team.position }} </p>
<p>{{ team.about }} </p>
{% endfor %}
And I'm getting what I want, comparing this to template looks like this
but I'm not getting positions of person, only company.Position.None and I've no idea how to get this. From documentation so far I know that it works for single ManyToManyField but I couldn't find example similar to mine case and I'm not sure how I should get (person's position)
Below are my files
models.py
from django.db import models
...
TYPES = (
('Startup', 'Startup'),
... )
CITIES = (
('Warszawa', 'Warszawa'),
... )
STACK = (
('PHP', 'PHP'),
... )
STUDENTS = (
('No', 'No'),
('Yes', 'Yes')
)
STACK_ICONS = (
('/static/icons/stack/php.png', 'PHP'),
('/static/icons/stack/javascript.png', 'JavaScript'),
...
)
POSITIONS = (
('CEO', 'Chief Executive Officer'),
('CTO', 'Chief Technology Officer'),
...
)
# object position with relationship many to many to person
class Position(models.Model):
position = models.CharField(max_length=50, choices=POSITIONS)
def __str__(self):
return self.position
# object person relation many to one (many persons to one company)
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
about = models.TextField(max_length=500, default=None)
position = models.ManyToManyField('Position')
photo = models.ImageField(blank=True, null=True, default=None)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
# object company
class Company(models.Model):
# field person with relation many to one (many persons to 1 company)
team = models.ManyToManyField('Person')
name = models.CharField(max_length=100, blank=False)
technologies = models.ManyToManyField('Stack')
website = models.TextField(max_length=150, blank=True, null=True, validators=[URLValidator()])
...
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Company, self).save(*args, **kwargs)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.name
# object stack relation manytomany with Company
class Stack(models.Model):
name = models.CharField(max_length=30, choices=STACK)
icon = models.CharField(max_length=80, choices=STACK_ICONS)
def __str__(self):
return self.name
views.py
from django.shortcuts import render, get_object_or_404, redirect
...
def brands(request, slug):
brand = get_object_or_404(Company, slug=slug)
return render(request, 'company/comp_view.html', {'brand': brand})
def stacks(request):
stack = get_object_or_404(Stack)
return render(request, 'company/comp_view.html', {'stack': stack})
def positions(request):
position = get_object_or_404(Position)
return render(request, 'company/comp_view.html', {'position': position})
...
comp_view.html
{% extends 'company/base.html' %}
<div class="col col-md-1"></div>
<div class="col col-md-5 card-section">
<div class="team ">
<div class="card-title">
<span>Team</span>
</div>
<div class="row text-center">
<div class="col col-md-4">
{% for team in brand.team.all %}
<p>{{ team.first_name }} {{ team.last_name }}</p>
<img class="img-thumbnail" src="/media/{{ team.photo }}">
<p>{{ team.position }}</p>
<p>{{ team.about }} </p>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
There's no such thing as a "single ManyToManyField". You have an m2m relationship, you need to iterate through it just like you do for the team members.
{% for position in team.position.all %}
{{ position.name }}
{% endfor %}
I am interested in displaying the detail template on the same page as the category template so that when a category is clicked its content(models) is displayed beside it.
something like this
Illustration
Currently the detail template is rendered independently after clicking a category link.
The models
class Category(models.Model):
cat_name = models.CharField(max_length = 200)
def __str__(self):
return self.cat_name
def get_models(self):
return Role_model.objects.filter(category=self)
class Role_model(models.Model):
category = models.ForeignKey(Category, related_name ='categories')
#photos = models.ImageField(upload_to ='users/%Y/%m/%d', blank = True)
name = models.CharField(max_length = 200, blank = False)
importers = models.IntegerField(blank = True, null = True)
def __str__(self):
return self.name
Views.py
def categories(request):
cats = Category.objects.all()
models = Role_model.objects.all()
return render(request, "category.html", {'cats':cats,'models':models})
def DetailView(request, cat_id):
#cat = get_object_or_404(Category, id = id)
return render(request,"detail.html", {'category':Category.objects.get(id = cat_id)})
urls
url(r'^$', views.categories, name='categories'),
url(r'^celebs/(?P\d+)/$', views.DetailView, name='detail'),
Category.html
<div class="categories">
{% for category in cats %}
<ul class = "breadcrumb">
<li>{{category.cat_name}}</li>
</ul>
</div>
{% endfor %}
Detail.html
<div class="content">
{% for model in category.get_models %}
<h5>{{model.name}}</h5>
{% endfor %}
</div>
I m beginner.
I'm trying to access a related item of the model Product in the template layer of a ProductDetailView. How can I retrieve the ImageFields of the Products' Brand's BrandImages? I have to traverse one forward and one reverse ForeignKey.
Edited to include get_logo_url
What is wrong with the get_logo_url function?
products/models.py
class Product(models.Model):
brand = TreeForeignKey('Brand', verbose_name='parent category', related_name='products', default='')
title = models.CharField(max_length=120)
description = models.TextField(max_length=500, blank=True, null=True)
price = models.DecimalField(decimal_places=2, max_digits=20)
active = models.BooleanField(default=True)
category = TreeForeignKey('Category', verbose_name='parent category', related_name='products', default='')
slug = models.SlugField(default='')
objects = ProductManager()
class Meta:
unique_together = ('slug', 'category')
def get_absolute_url(self):
return reverse("product_detail", kwargs={"pk":self.pk})
def __unicode__(self):
return self.title
def get_image_url(self):
img = self.productimage_set.first()
if img:
return img.image.url
return img
brands/models.py
def image_upload_to(instance, filename):
title = instance.brand.title
slug = slugify(title)
file_extension = filename.split(".")[1]
new_filename = "%s.%s" % (instance.id, file_extension)
return "products/%s/%s" % (slug, new_filename)
class BrandImage(models.Model):
brand = models.ForeignKey('Brand', related_name='brandimages')
is_slider = models.BooleanField(default=False)
is_featured = models.BooleanField(default=False)
is_logo = models.BooleanField(default=False)
image = models.ImageField(upload_to=image_upload_to)
def __unicode__(self):
return self.brand.title
def get_logo_url(self):
if is_logo:
img = self.brandimage_set.first()
if img:
return img.image.url
return img
def thumb(self):
if self.image:
return u'<img src="%s" width=120 height=120 />' % (self.image.url)
else:
return u'No image file found'
thumb.allow_tags = True
class Brand(MPTTModel):
title = models.CharField(max_length=50, default='')
parent = TreeForeignKey('self', null=True, blank=True, verbose_name='parent brand', related_name='brands')
slug = models.SlugField(unique=True)
def get_absolute_url(self):
return reverse('brands', kwargs={'path': self.get_path()})
def __unicode__(self):
return self.title
template
<div class="rightpart">
<div class="prodbrand h2">
<h1>{{ product.brand }}</h1>
<div class="brandlogo">
{% for image in product.brand.brandimages.all %}
<img src="{{image.get_logo_url }}"/>
{% endfor %}
</div>
</div>
<div class="prodtitle"><h2>{{ product.title }}</h2>
</div>
views.py
class ProductDetailView(DetailView):
model = Product
template_name = 'products/product.html'
def get_context_data(self , *args , **kwargs):
context = super(ProductDetailView , self).get_context_data(*args,**kwargs)
instance = self.get_object()
context["related"] = Product.objects.get_related(instance)
return context
urls.py
url(r'^$', ProductDetailView.as_view(), name='products'),
is there a way to access foreign fields in django templates like this?
As you are using a ListView to display your products there's several things to notice:
get_context_data() must return a dictionary: return context is missing
super().get_context_data should be called with *args,**kwargs incase you decide to subclass the ProductListView at a later point in time.
super().get_context_data will contain a object_list key which contains the list of objects returned by get_queryset(), in your case objects of class Product.
When accessing a property from a template, django will attempt to call it without parameters if it is callable. This is often useful e.g.: for {{ request.user.is_authenticated }} or product.brand.brandimages.all
Your template should look like this:
product_list.html
{% for product in object_list %}
<div class="rightpart">
<div class="prodbrand h2">
<h1>{{ product.brand }}</h1>
<div class="brandlogo">
{% for image in product.brand.brandimages.all %}
<img src="{{image.image.url}}"/>
{% endfor %}
</div><!-- End brandlogos -->
</div><!-- End prodbrand -->
<div class="prodtitle">
<h2>{{ product.title }}</h2>
</div>
</div><!-- End rightpart -->
{% endfor %}
Take into account that this will incur several database lookups from the template. You generally want to avoid the case that your presentation layer reaches into the database, which is why you should prefer to do the database lookup in the corresponding View. Also for property access consider using select_related and prefetch_related as appropriate to avoid unneeded database queries.
views.py
class ProductListView(ListView):
model = Product
queryset = Product.objects.all().active()
def get_context_data(self, *args, **kwargs):
context = super(ProductListView, self).get_context_data(*args, **kwargs)
context["now"] = timezone.now()
return context
def get_queryset(self, *args, **kwargs):
# We call super() for potential subclasses
queryset = super(ProductListView, self).get_context_data(*args, **kwargs)
queryset.prefetch_related('brand__brandimages')
return queryset