I have in project two classes - Photo and Prescription inside models.py file which are related each other with foreign key. Here is part of the code:
class Photo(models.Model):
name = models.CharField(max_length=100,null=False)
photo = models.ImageField(upload_to="photos/",null=True)
def photo_tag(self):
return '<img src="/media/{0}">'.format(self.photo)
photo_tag.short_description = 'Photo of prescription'
photo_tag.allow_tags = True
class Prescription(models.Model):
description = models.CharField(max_length=100,null=True)
photo = models.ForeignKey(Photo, related_name='related_photo',null=True)
def __str__(self):
return self.description
And my Admin.py
class PhotoAdmin(admin.ModelAdmin):
list_display = ('name', 'photo_tag')
fields = ('name','photo','photo_tag')
readonly_fields = ('photo_tag',)
admin.site.register(Photo,PhotoAdmin)
class PrescriptionAdmin(admin.ModelAdmin):
list_display = ('get_photo')
fields = ('photo','description')
model = Prescription
def get_photo(self, obj):
return obj.photo.photo_tag
get_photo.short_description = 'Photo of prescription'
admin.site.register(Prescription,PrescriptionAdmin)
Question is, when I open prescriptions list instead of photo in the Photo of prescription field shows following message.
<bound method Photo.photo_tag of <Photo: Photo object>>
How real photo could be described there?
If you are going to render the image in html (which I guess you are), could you not then use ginger to display the description in a similar way to this?
<img src="{{photo.url}" title="{{photo.short_description}}" alt="{{photo.short_description}}">
Note that I included the alt for internet explorer which seems to use that instead of title.
There are few wrong issues with your approach. Your photo_tag method should be a property method, or eventually cached_property method. For displaying safely HTML code you should use the method format_html provided by Django.
Here is how I'd refactor your code:
models.py
from django.db import models
from django.utils.html import format_html
from django.utils.functional import cached_property
class Photo(models.Model):
name = models.CharField(max_length=100, null=False)
photo = models.ImageField(upload_to="photos/", null=True)
# better use blank=True instead of null=True for ImageField
#cached_property
def photo_tag(self):
if self.photo:
return format_html(
'<img src="{img}">',
img=self.photo.url
)
return None # or better return '' if you use blank=True
photo_tag.short_description = 'Photo of prescription'
class Prescription(models.Model):
description = models.CharField(max_length=100, null=True)
photo = models.ForeignKey(Photo, related_name='related_photo', null=True)
def __str__(self):
return self.description
Now you can use photo_tag as a property of Photo and bind the HTML safe in your admin.
Thanks anybody who tried to help. I figured out another solution with help of #Klaus who answered first and I am pretty sure that there is maybe much more better solutions also. Here are how I changed my code
Models.py
from django.utils.safestring import mark_safe
class Photo(models.Model):
name = models.CharField(max_length=100,null=False)
photo = models.ImageField(upload_to="photos/",null=True)
def photo_tag(self):
return mark_safe('<img src="/media/{0}">'.format(self.photo))
photo_tag.short_description = 'Photo of prescription'
photo_tag.allow_tags = True
class Prescription(models.Model):
description = models.CharField(max_length=100,null=True)
photo = models.ForeignKey(Photo, related_name='related_photo',null=True)
def __str__(self):
return self.description
Admin.py
class PhotoAdmin(admin.ModelAdmin):
list_display = ('name', 'photo_tag')
fields = ('name','photo','photo_tag')
readonly_fields = ('photo_tag',)
admin.site.register(Photo,PhotoAdmin)
class PrescriptionAdmin(admin.ModelAdmin):
list_display = ('get_photo')
fields = ('photo','description')
model = Prescription
def get_photo(self, obj):
return obj.photo.photo_tag()
get_photo.short_description = 'Photo of prescription'
admin.site.register(Prescription,PrescriptionAdmin)
Related
I try to do tabular inline admin.
In the child tab, if we include a ForeignKey field, it will show the str property of that foreign model.
But how to also show another property of that foreign model?
Here is my models.py
class RawMaterial(models.Model):
name = models.CharField(max_length=15)
ubuy = models.CharField(max_length=5)
usell = models.CharField(max_length=5)
uconv = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.name
class Coctail(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Ingredient(models.Model):
coctail = models.ForeignKey(Coctail,
related_name='ingredient',
on_delete=models.CASCADE)
rawmaterial = models.ForeignKey(RawMaterial,
related_name='ingredient',
on_delete=models.CASCADE)
qty = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.rawmaterial
def rawusell(self):
return self.rawmaterial.usell
rawusell.short_description = 'UOM'
Here is my admin.py
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(RawMaterial)
class IngredientInline(admin.TabularInline):
model = Ingredient
list_display = ('rawmaterial', 'qty', 'rawusell')
class CoctailAdmin(admin.ModelAdmin):
inlines = [IngredientInline]
admin.site.register(Coctail, CoctailAdmin)
and here what I got
My question is : How to show rawmaterial.usell in Ingredient tab?
Sincerely
-bino-
You can show the rawmaterial.usell field in the ingredient tab but it will not be editable. Since, any field can only be editable if they are a field of that model (Without using any custom form and logic).
So, if you want rawmaterial.usell to be editable, you will have to make a rawmaterial admin
You can show the rawmaterial.usell in IngredientInline by doing this.
class IngredientInline(admin.TabularInline):
model = Ingredient
readonly_fields = ('rawusell', )
list_display = ('rawmaterial', 'qty', 'rawusell')
def rawusell(self, obj):
return obj.rawmaterial.usell
This will start showing usell in the inline admin.
I'm having trouble finding the best way to override and add custom html to an edit/add model form in my Django admin site.
Here are the two models involved here:
Icon model used to store "Font Awesome" Icons:
class Icon(models.Model):
name = models.CharField(max_length=100, null=False)
style = models.CharField(max_length=10, choices=STYLE_CHOICES, null=False)
retired = models.BooleanField(default=False)
def delete(self):
self.retired = True
self.save()
objects = NotRetiredManager()
objects_deleted = DeletedManager()
def __str__(self):
return self.name
Workbook model that holds foreign key reference to the above Icon model:
class Workbook(models.Model):
client = models.ForeignKey(Client, on_delete=models.SET_NULL, null=True)
icon = models.ForeignKey(Icon, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=100)
workbookLink = models.CharField(max_length=1000)
retired = models.BooleanField(default=False)
def delete(self):
self.retired = True
self.save()
objects = NotRetiredManager()
objects_deleted = DeletedManager()
def __str__(self):
return self.name
Here are the overridden admin models for the above models:
class BaseAdmin(AdminImageMixin, admin.ModelAdmin):
def delete_queryset(self, request, queryset):
for obj in queryset:
obj.delete()
#admin.register(Workbook)
class WorkbookAdmin(BaseAdmin):
list_display = ("name", "client")
list_filter = (NameFilter, ClientNameFilter)
ordering = ("name", )
#admin.register(Icon)
class IconAdmin(BaseAdmin):
fields = ("name", "style", "icon_display")
list_display = ("icon_display", "name", "style" )
list_display_links = ("icon_display", "name")
list_filter = (NameFilter, )
ordering = ("name", )
def icon_display(self, obj):
return mark_safe(f'<i class="{obj.style}{obj.name}"></i>')
readonly_fields = ["icon_display"]
Here is a list display of some Icons I have in my database:
Currently, the add/edit page for a Workbook on my Admin Site looks like this:
I would like for that dropdown in that second screenshot to be customized similar to the "Icon Display" column in that first screenshot so that a user would choose from graphical list of icons as opposed to the default choicefield form containing the Icon names.
I've looked into the Django docs as well as similar questions on here such as this Similar Stack Overflow Question; however, I'm not fully understanding the proper way to implement something like this.
I hope the information I provided about my app is useful. Please let me know if you'd like me to provide any additional information, or add any clarifications!
Let's say I have models:
from filer.fields.image import FilerImageField()
class Image(models.Model):
name = models.CharField(max_length=128)
image = FilerImageField()
class City(models.Model):
name = models.CharField(max_length=128)
images = models.ManyToManyField(Image, blank=True)
admin.py:
class ImageAdmin(admin.ModelAdmin):
def thumb(self, obj):
return '<img src=%s />' %obj.image.icons['64']
thumb.short_description = 'Preview'
thumb.allow_tags = True
list_display = ('image', 'name', 'thumb', )
class CityAdmin(models.ModelAdmin):
filter_horizontal = ('images', )
admin.site.register(Image, ImageAdmin)
admin.site.register(City, CityAdmin)
Questions:
1. How to add "+" button in CityAdmin above m2m widget? Currently what i have:
2. Is it possible in CityAdmin m2m widget have some list display where I can see image thumbnail? Ideally like in ImageAdmin:
What I have tried:
In Image model __unicode__ function return '<img src=%s />' %self.image.icons['64'], but then in admin it just displays raw text.
I have the following code:
models.py
from django.db import models
from parler.models import TranslatableModel, TranslatedFields
class Federation(TranslatableModel):
translations = TranslatedFields(
name = models.CharField('name', max_length=50)
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Athlete(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
federation = models.ForeignKey('Federation', on_delete=models.SET_NULL, null=True)
height = models.IntegerField();
weight = models.IntegerField();
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
admin.py
from django.contrib import admin
from parler.admin import TranslatableAdmin
from .models import Athlete, Federation
class AthleteAdmin(admin.ModelAdmin):
list_display = ['first_name', 'last_name', 'height', 'weight', 'get_federation_name']
fields = ['first_name', 'last_name', 'height', 'weight', 'federation']
def get_federation_name(self, obj):
obj.federation.set_current_language('en')
return obj.federation.name
get_federation_name.short_description = 'Federation'
class FederationAdmin(TranslatableAdmin):
search_fields = ['translations__name']
list_display = ['name']
fields = ['name']
admin.site.register(Federation, FederationAdmin)
admin.site.register(Athlete, AthleteAdmin)
The federation field is shown as a list but the text in the select menu is shown as "Federation object." For the list, I created a function to fetch the data from related Federation model's translation relation. I want to do the same with the form fields. If I get this to work in form fields without a function, I will also change the list display to work the same way.
I am new to Python and Django (first time) and I can't seem to find a solution to this problem.
Thank you!
By default it uses the __str__() method of the object. So the easiest way to change this is to set this method. For instance:
class Federation(models.Model):
...
def __str__(self):
return "{0} ({1})".format(self.translation, self.created_at)
Another way — if you don't want to override the __str__ method — would be to override the label_from_instance of the form Field itself. But it's more tricky.
def _federation_label_from_instance(self, obj):
return "{0} ({1})".format(obj.translation, obj.created_at)
class AthleteAdmin(admin.ModelAdmin):
...
def formfield_for_foreignkey(self, db_field, request, **kwargs):
formfield = super().form_field(db_field, request, **kwargs)
if db_field.name == 'federation':
formfield.label_from_instance = _federation_label_from_instance
return formfield
I'm trying to save some form data inputted by the user. I would like to slugify the "name" which was entered by the user, but dont want the slug field to show on the template that the user sees. I tried to do it manually with the sell function that you see below, but cant quite get it to work. I want to eventually save the slugified name into the Item model I have listed below. I'm sure there's a much smarter/simpler way than the sell function I'm currently using :P. Thanks in advance!
class Item(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=75)
slug = models.SlugField(max_length=50, unique=True)
is_active = models.BooleanField(default=True)
image = models.CharField(max_length=50)
price = models.DecimalField(max_digits=9, decimal_places=2)
quantity = models.IntegerField(default=1)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
shipping_price = models.DecimalField(decimal_places=2, max_digits=6)
categories = models.ManyToManyField(Category)
class AddItem(forms.ModelForm):
class Meta:
model = Item
exclude = ('user','slug','is_active',)
def sell(request):
if request.method == "POST":
form = AddItem(request.POST)
item = form.save(commit=False)
item.user = request.user
item.is_active = True
item.slug = slugify(form.name) **#not sure what this line should be?**
item.save()
if form.is_valid():
form.save()
return HttpResponseRedirect('thanks.html')
else:
url = urlresolvers.reverse('register')
return HttpResponseRedirect(url)
You can exclude slug from user form.
And slugify in pre_save signal.
from django.dispatch import receiver
from django.db.models.signals import pre_save
#receiver(pre_save, sender=Item)
def iter_pre_save_handler(sender, instance, **kwargs):
if not instance.pk:
instance.slug = slugify(instance.name)
According to the docs, you can exclude a field from being rendered in a model form like this:
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title')
or
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
exclude = ('birth_date',)
or by setting editable=False on the Field instance in your model.
Once you have done this, you can override the save method of the model, as the comments in the OP have suggested:
# shamelessly copied from http://stackoverflow.com/questions/837828/how-do-i-create-a-slug-in-django/837835#837835
from django.template.defaultfilters import slugify
class test(models.Model):
q = models.CharField(max_length=30)
s = models.SlugField()
def save(self, *args, **kwargs):
self.s = slugify(self.q)
super(test, self).save(*args, **kwargs)