How can I add a related object to the Admin index view? - django

I want to be able to show if an Image has been associated with each Product from the list_display view.
I seem to be having trouble because I'm dealing with an associated object.
models.py
class ImageMain(models.Model):
"""This is the Main Image of the product"""
product = models.ForeignKey(Product)
photo = models.ImageField(upload_to='lcdtvs')
pub_date = models.DateTimeField('date published', auto_now_add=True)
class Product(models.Model):
name = models.CharField(max_length=100)
poll = models.ForeignKey(Poll)
pub_date = models.DateTimeField('date published', auto_now_add=True)
size = models.IntegerField(default=0)
admin.py
def photo_for_product(obj):
images = obj.imagemain_set.all()
return images[0].photo
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', "photo_for_product")
inlines = [DescriptionInline, FeatureInline, AffiliateInline]
def upper_case_name(self, obj):
return ("%s" % (obj.name)).upper()
def photo_for_product(self, obj):
images = self.imagemain_set.all()
return images[0].photo
admin.site.register(ImageMain)
admin.site.register(Product, ProductAdmin)
For some reason, the upper_case_name() displays fine in the list view.
The photo_for_product() just keeps displaying (None).
I also tried use pdb in the photo_for_product method, but Django doesn't like that.
I also tried to put the callable before the ModelAdmin method, however, that created a lot of errors:
ProductAdmin.list_display[1], 'photo_for_product' is not a callable or an attribute of 'ProductAdmin' or found in the model 'Product'.

It's not clear from your question exactly what you want the output to be. You say you want to know "if an image has been associated" - if so, you could try this:
def photo_for_product(self, obj):
images = self.imagemain_set.all()
if images:
return True
else:
return False
photo_for_product.boolean = True
If you want to actually see the image, you'll need to return some HTML that renders it in an img tag.
def photo_for_product(self, obj):
images = self.imagemain_set.all()
if images:
return '<img src="%s">' % images[0].photo.url
else:
return ''
photo_for_product.allow_tags = True

Write a method that returns the necessary information as a string and add the name of the method to list_displays on your admin class.

I wanted the output to be a string of the path to the image. Apparently, the issue is that images[0].photo is am ImageFieldFile and not a string.
It seems that by default, ImageFieldFile has the following attributes:
ImageFieldFile.name
ImageFieldFile.path
ImageFieldFile.url
All those attributes return unicode strings.

Related

Django - 'QuerySet' object has no attribute 'künstler_set'

I have a relation between an artist and images. I want to show a single artist and his related images. I created two models for artist and image. Inside the image class I made a ForeignKey to artist:
class Artist(models.Model):
profilepic = models.ImageField( blank=True)
surname = models.CharField(max_length=120)
class Image(models.Model):
image = models.ImageField()
relArtist = models.ForeignKey(Artist, null=True, on_delete=models.SET_NULL)
inside my views.py I tried to get the artist by ID (primaryKey) which is working perfectly but it didn't work with the images.
def artistSingle(request, pk):
artist = Artist.objects.filter(id=pk)
images = artist.relArtist_set.all()
return render(request, 'artists/artistSingle.html', {
'artist': artist,
'images': images,
})
If I run the code it throws an error:
'QuerySet' object has no attribute 'relArtist_set'
I dont know how to get the images which rely to the artist. Does someone have a solution?
You should first get your artist with get() method ( filter returns QuerySet)
artist = Artist.objects.get(id=pk)
You should use get and not filter, filter returns QuerySet and it is meant to return multiple artist objects) and your actual artist in this case would be artist = Artist.objects.filter(id=pk)[0]
Default is relatedmodelname_set as documented so:
images = artist.image_set.all()
It can be set to different value by using related_name attribute
relArtist = models.ForeignKey(Artist, null=True, on_delete=models.SET_NULL related_name='images')
images = artist.images.all()
images = Image.objects.filter(relArtist=artist)
Normally, the Artist would not be aware of any images tied to it, so you should check for the images whose artist match the one you have.
However, Django provides a Manager to reverse the foreign key functionality, making the Artist aware of the images tied to it.
images = artist.image_set.all()
Please try the following one. It will resolve your problem.
def artistSingle(request, pk):
artist_queryset = Artist.objects.filter(id=pk)
artist = artist_queryset.first() if artist_queryset.first() else None
try:
images = artist.image_set.all()
except Exception as exp:
images = []
return render(request=request, template_name='artists/artistSingle.html', context={
'artist': artist,
'images': images,
})

How to get the slug STRING value in django models?

Basically, I have 3 classes (Vehicle, Car and Motorcycle, these 2 last extend the first one)... In the Vehicle class there's the main_image attribute which is a models.ImageField type, as you can see bellow:
class Vehicle(models.Model):
def __str__(self):
return self.title
[...]
main_image = models.ImageField(
upload_to = 'media/images/' + str(slug) + '/', verbose_name = 'Foto principal',
null = False
)
So, the str(slug) doesn't work properly, 'cause when I upload an image, it is always uploaded to
/media/images/<django.db.models.fields.SlugField>
when it should actually upload to
/media/images/(object-slug-value)/
I've tried many different things but any of them worked the way I wanted.
How can I get the string value from the slug attribute?
The slug you are using is the field in your model, not the instance. upload_to takes a callable, which you can use to build the path:
def get_path(instance, filename):
return 'media/images/{}/{}'.format(instance.slug, filename)
class Vehicle(models.Model):
.
.
.
main_image = models.ImageField(upload_to=get_path)

Django Admin showing thumbnail of foreign key's imagefield in inline form

I have a simple structure, with a Product model, and AppMedia model and a ProductMedia join table.
Product (has many) ProductMedia
ProductMedia (has one) AppMedia
What I would really like is to see the thumbnail for AppMedia's media field in an inline form in Product.
e.g. edit Product in Django admin, shows a StackedInline form. This contains (at present) a drop down (select) of all AppMedia.
What I need is the thumbnail.
Any help appreciated.
I am certain this isn't difficult, but I'm struggling. It's quite straight forward to put a thumbnail in for the AppMedia form (which is where the media ImageField resides), but not for the ProductMedia form which is using AppMedia as a ForeignKey.
Basic Models...
class Product(models.Model):
name = models.CharField(max_length=100)
class AppMedia(models.Model):
media = models.ImageField(upload_to=appmedia_upload_to) #appmedia_upload_to defined elsewhere)
class ProductMedia(models.Model):
title = models.CharField(max_length=150)
media = models.ForeignKey(AppMedia)
media_order = models.IntegerField(default=0)
product = models.ForeignKey(Product)
The AppMedia is shared in this way to stop multiple uploads of the same file and also to store extra meta-data (not displayed here) with the image.
I had the same issue. I hope at least one of my attempts will be usable for your case:
My first attempt to solve this problem (without overriding ForeignKeyRawIdWidget) was to:
set form property in inline admin where thumbnail should be displayed
add another field into given form class with widget, which will display a thumbnail
However I abandoned this solution because I think I have to inject data about thumbnail into given field in form's constructor and I don't consider it a nice solution.
My next solution was to use MultiWidget for some field. In this case I don't need to add another field to form and I'll have data required to display thumbnail in widget without any need to inject them in constructor.
class ThumbnailMultiWidget(MultiWidget):
def decompress(self, value):
#has to be overriden
return [None,None]
class ThumbnailWidget(Widget):
def render(self, name, value, attrs=None):
#not quite sure what is in `value`, I've not been so far
return '<img src="%s"/>' % value.url
class PhotoInlineForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PhotoInlineForm, self).__init__(*args, **kwargs)
wdgts = [self.fields['media'].widget, ThumbnailWidget()]
self.fields['media'].widget = ThumbnailMultiWidget(widgets=wdgts)
class Meta:
model = RecipePhoto
But I abandoned this solution as well, because I found out, that there's actually an instance's representation in ForeignKeyRawIdWidget (which is widget I use) with all data I need to show the thumbnail. And that is my final solution:
So because my inline items have raw_id_field for choosing an inline record, I could simply override method label_for_value in ForeignKeyRawIdWidget, which is used to represent existing inline record. Usually it's __unicode__ (I think). I inherited ForeignKeyRawIdWidget and overriden this method to show image thumbnail:
class PhotoForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
def label_for_value(self, value):
key = self.rel.get_related_field().name
try:
obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
except (ValueError, self.rel.to.DoesNotExist):
return ''
else:
"""
there's utilized sorl.thumbnail, but you can return st like this:
<img src='%s' /> % obj.media.url
"""
return Template("""{% load thumbnail %}
{% thumbnail image.image "120x120" crop="center" as one %}
<img src="{{ one.url }}" />
{% endthumbnail %}""").render(Context({
'image': obj
}))
class AppMediaInlineAdmin(admin.TabularInline):
model = AppMedia
extra = 1
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'media':
db = kwargs.get('using')
kwargs['widget'] = PhotoForeignKeyRawIdWidget(db_field.rel, self.admin_site, using=db)
return super(AppMediaInlineAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
You should look into the related_name parameter for foreign keys, which will allow you to access the ProductMedia in reverse, i.e. if you change the ProductMedia model to:
class ProductMedia(models.Model):
title = models.CharField(max_length=150)
media = models.ForeignKey(AppMedia)
media_order = models.IntegerField(default=0)
product = models.ForeignKey(Product, related_name='media')
You could access the media object in Product model, which will allow you to put it in your admin in-line form. i.e you would have (for an easier explanation I've put the ImageField in ProductMedia):
class Product(models.Model):
name = models.CharField(max_length=100)
def admin_image(self):
return '<img src="%s"/>' % (self.media.all()[0].image.url)
admin_image.allow_tags = True
class ProductMedia(models.Model):
title = models.CharField(max_length=150)
image = models.ImageField(upload_to=appmedia_upload_to) #appmedia_upload_to defined elsewhere)
media_order = models.IntegerField(default=0)
product = models.ForeignKey(Product, related_name='media')
Then in your admin.py put:
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'admin_image')
admin.site.register(models.Product, ProductAdmin)
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
Hope I understood your question correctly and that helped. Also the code isn't tested but I'm pretty sure it should work.

ORM update does not support file upload

I have following model
class Category(models.Model):
name = models.CharField(max_length=30)
is_active=models.BooleanField()
photo=models.ImageField(upload_to='category')
def __unicode__(self):
name = str(self.name)
return name
class Meta:
permissions = (
('category','Category'),
('view_category', 'View category'),
)
My form class is as follows
class categoryForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={'class':'box'}),max_length=32,label='Category',required=True)
is_active = forms.BooleanField(required=False)
id = forms.CharField(widget=forms.HiddenInput,required=False)
photo = forms.FileField(
required=False,
label='Select an Image',
help_text='max. 4 megabytes'
)
In view.py I have the following
formdata = categoryForm(request.POST,request.FILES)
if formdata.is_valid():
cd = formdata.cleaned_data
p1=Category()
p1.id=cd['id']
p1.name=cd['name']
p1.is_active=cd['is_active']
p1.photo=cd['photo']
p1.save()
It is working fine but when i changed it to the following, image is not uploaded
Category.objects.filter(id='%s'%(cd['id'])).update(name='%s'%(cd['name']),
is_active='%s'%(cd['is_active']),
photo=cd['photo']
)
I think the second method is faster way, but why it is not working for ImageField
the id in the code pasted above is a string, try converting it to an int. That will i guess make the filter query work and hence make the update on it work as well.
You shouldn't be passing a string to is_active. Just pass the boolean and it should work. Also, you shouldn't be writing code like '%s' % string; you can just use string
You need to use get_or_create

ForeignKey field will not appear in Django admin site

A foreign key on a model is not appearing in the Django admin site. This is irrespective of whether the field is explicitly specified in a ModelAdmin instance (fields = ('title', 'field-that-does-not-show-up')) or not.
I realize there are many variables that could be causing this behavior.
class AdvertiserAdmin(admin.ModelAdmin):
search_fields = ['company_name', 'website']
list_display = ['company_name', 'website', 'user']
class AdBaseAdmin(admin.ModelAdmin):
list_display = ['title', 'url', 'advertiser', 'since', 'updated', 'enabled']
list_filter = ['updated', 'enabled', 'since', 'updated', 'zone']
search_fields = ['title', 'url']
The problem is the advertiser foreign key is not showing up in the admin for AdBase
class Advertiser(models.Model):
""" A Model for our Advertiser
"""
company_name = models.CharField(max_length=255)
website = models.URLField(verify_exists=True)
user = models.ForeignKey(User)
def __unicode__(self):
return "%s" % self.company_name
def get_website_url(self):
return "%s" % self.website
class AdBase(models.Model):
"""
This is our base model, from which all ads will inherit.
The manager methods for this model will determine which ads to
display return etc.
"""
title = models.CharField(max_length=255)
url = models.URLField(verify_exists=True)
enabled = models.BooleanField(default=False)
since = models.DateTimeField(default=datetime.now)
expires_on=models.DateTimeField(_('Expires on'), blank=True, null=True)
updated = models.DateTimeField(editable=False)
# Relations
advertiser = models.ForeignKey(Advertiser)
category = models.ForeignKey(AdCategory)
zone = models.ForeignKey(AdZone)
# Our Custom Manager
objects = AdManager()
def __unicode__(self):
return "%s" % self.title
#models.permalink
def get_absolute_url(self):
return ('adzone_ad_view', [self.id])
def save(self, *args, **kwargs):
self.updated = datetime.now()
super(AdBase, self).save(*args, **kwargs)
def impressions(self, start=None, end=None):
if start is not None:
start_q=models.Q(impression_date__gte=start)
else:
start_q=models.Q()
if end is not None:
end_q=models.Q(impression_date__lte=end)
else:
end_q=models.Q()
return self.adimpression_set.filter(start_q & end_q).count()
def clicks(self, start=None, end=None):
if start is not None:
start_q=models.Q(click_date__gte=start)
else:
start_q=models.Q()
if end is not None:
end_q=models.Q(click_date__lte=end)
else:
end_q=models.Q()
return self.adclick_set.filter(start_q & end_q).count()
class BannerAd(AdBase):
""" A standard banner Ad """
content = models.ImageField(upload_to="adzone/bannerads/")
The mystery deepens. I just tried to create a ModelForm object for both AdBase and BannerAd, and both generated fields for the advertiser. Some crazy admin things going on here...
I believe I've just run into exactly the same problem, but was able to debug it thanks to the help of persistent co-workers. :)
In short, if you look in the raw HTML source you'll find the field was always there - it's just that:
Django tries to be clever and put the form field inside a div with CSS class="form-row $FIELD_NAME",
The field's name was "advertiser", so the CSS class was "form-row advertiser",
...Adblock Plus.
Adblock Plus will hide anything with the CSS class "advertiser", along with a hell of a lot of other CSS classes.
I consider this a bug in Django.
maybe it is an encode error. I had the same problem, but when i added # -- coding: UTF-8 -- in the models.py, all fine.
Another very dumb cause of the same problem:
If there is only one instance of the related model, then the filter simply won't show. There is a has_output() method in RelatedFieldListFilter class that returns False in this case.
It's a strange problem for sure. On the AdBase model if you change
advertiser = models.ForeignKey(Advertiser)
to
adver = models.ForeignKey(Advertiser)
then I believe it'll show up.
Powellc, do you have the models registered with their respective ModelAdmin class?
admin.site.register(Advertiser, AdvertiserAdmin) after the ModelAdmin definitions.
You are talking about the list_display option, right?
Is the unicode-method for your related model set?
If the field is a ForeignKey, Django
will display the unicode() of the
related object
Also check this thread for some hints: Can "list_display" in a Django ModelAdmin display attributes of ForeignKey fields?
Try disabling your ad blocker. No, this is not a joke. I just ran into this exact problem.
We just ran into this problem.
It seems that if you call you field advertiser the in the admin the gets given an 'advertiser' class.
Then is then hidden by standard ad blocking plugins. If you view source your field will be there.