Showing value (URL/Thumbnail) from Foreign key in Admin - django

I want to show an small preview image in Django Admin. I have made this hack, which works when the key is in the actual model itself.
class Product(models.Model):
prod_name = models.CharField ("Name", max_length=130)
image = models.URLField(max_length=340, blank=True, null=true)
def admin_image(self):
return '<center><img src="%s"/width="100px"></center>' %(self.image, self.image)
admin_image.allow_tags = True
However, I want it to show an image (read a URL) from a Foreign Key. I tried the following but no luck:
class Product_Option(models.Model):
colour = models.CharField (max_length=80, blank=True, null=True)
size = models.CharField (max_length=80, blank=True, null=True)
image_default = models.URLField(max_length=340, blank=True, null=True) # SHOW this image by
class Product(models.Model):
prod_name = models.CharField ("Name", max_length=130)
image = models.URLField(max_length=340, blank=True, null=true)
Default_Image = models.ForeignKey(Product_Option, blank=True, null= True)
Admin.py
class ProductAdmin(ImportExportModelAdmin):
resource_class = ProductResource
def admin_image(self, obj):
return '<center><img src="%s"/width="100px"></center>' %(obj.Stock_Image.image_default.url, obj.Stock_Image.image_default.url)
admin_image.allow_tags = True
list_display = ('prod_name','admin_image')
readonly_fields = ('admin_image',)

Your code is a little confusing, and you should be careful about putting HTML type code in your models. That being said, assuming you are trying to add thumbnails to your admin via foreignkey relations, this would be the easiest approach:
from django.utils.html import format_html
class ProductAdmin(ImportExportModelAdmin):
resource_class = ProductResource
list_display = ('prod_name', 'admin_image')
readonly_fields = ('admin_image',)
def admin_image(self, obj):
return format_html('<center><img src="{1}"/width="100px"></center>', obj.Default_Image.image_default, obj.Default_Image.image_default)
admin_image.allow_tags = True
Note: Notice the use of format_html(). Always use it in these cases to avoid vulnerabilities, as it escapes possibly malicious code.
Also, you were trying to use image_default.url, which only exists on an ImageField, not a URLField. I removed that as well in favor of just image_default.

Related

Django - Linking Models

I am using django and I want to link two models. The first model is comment and the second model is image. I want to have multiple images for one comment and an image should be linked with only one comment.
Comment model has its fields and image model looks like this:
class Image(models.Model):
image = models.ImageField(upload_to=f'{hash(id)}/', null=True, blank=True)
def __str__(self):
return self.image.name
And this is the model that I used to link comment and image:
class CommentImage(models.Model):
comment = models.OneToOneField(Comment, models.CASCADE, null=True)
image = models.ForeignKey(Image, models.CASCADE, null=True)
class Meta:
ordering = ["-id"]
verbose_name = _("Image")
verbose_name_plural = _("Images")
def __str__(self):
return self.image.image.name
Here is the admin panel of django:
enter image description here
As you can see I could be able add only one image and there is no button as well to add multiple image. What should I change to be able to add multiple images?
I have tried using ManytoManyField and removing comment field from CommentImage but it did not work.
I think you are overcomplicating things. Why not just add a text field to your ImageComment:
class Image(models.Model):
def upload_to(self, filename):
return f'{hash(self)}/{filename}'
image = models.ImageField(upload_to=upload_to, null=True, blank=True)
comment = models.ForeignKey(
'ImageComment', on_delete=models.SET_NULL, null=True
)
def __str__(self):
return self.image.name
class CommentImage(models.Model):
comment = models.TextField()
Or in case an Image can have multiple ImageComments as well, use a ManyToManyField:
class Image(models.Model):
def upload_to(self, filename):
return f'{hash(self)}/{filename}'
image = models.ImageField(upload_to=upload_to, null=True, blank=True)
comments = models.ManyToManyField(
'ImageComment'
)
def __str__(self):
return self.image.name
class CommentImage(models.Model):
comment = models.TextField()
You can even add an InlineModelAdmin to make editing comments at the same location as the image possible:
from django.contrib import admin
class ImageCommentInline(admin.TabularInline):
model = ImageComment
#admin.site.register(Image)
class ImageAdmin(admin.ModelAdmin):
inlines = [
ImageCommentInline,
]
You can try using this via manytomanyfields:
class CommentImage(models.Model):
comment = models.ForeignKey(Comment, models.CASCADE)
image = models.ForeignKey(Image, models.CASCADE)
class Comment(models.Model):
# other fields here
images = models.ManyToManyField(Image, through='CommentImage', related_name='comments')

show related fields in django admin panel

I have 3 django models. Requirement Model has its own fields. RequirementImage and RequirementDOc models have Requirement as foreign key in them which are used for multiple image and multiple document upload. In admin ,I want to show the Requirement along with the images and documents related to requirement. How can i show it in admin panel.
i want to show a view where i can list all fields of Requirement and RequirementImages and RequirementDocs together.
Below is the exact code of models.
class Requirement(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length = 5000)
mobile = models.CharField(max_length=15, null=True, blank=True)
email = models.EmailField(null=True, blank=True)
city = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class RequirementImage(models.Model):
requirement = models.ForeignKey('Requirement', on_delete=models.CASCADE)
image = models.ImageField(null=True, blank=True, validators=[
FileMimeValidator()
], upload_to=settings.MEDIA_RELATIVE_ROOT + "requirements/images")
class RequirementDoc(models.Model):
requirement = models.ForeignKey('Requirement', on_delete=models.CASCADE)
requirement_file = models.FileField(null=True, blank=True, upload_to=settings.MEDIA_RELATIVE_ROOT + "requirements/docs")
Python version is 3.7.12 and django version is 3.2.14
in models.py
from django.utils.safestring import mark_safe
class RequirementImage(models.Model):
requirement = models.ForeignKey('Requirement', on_delete=models.CASCADE)
image = models.ImageField(null=True, blank=True, validators=[
FileMimeValidator()
], upload_to=settings.MEDIA_RELATIVE_ROOT + "requirements/images")
def photo_tag(self):
return mark_safe('<img src="/your_path/{0}">'.format(self.image))
photo_tag.short_description = 'Photo of prescription'
photo_tag.allow_tags = True
and when you want to use it in admin
list_display = ('get_photo')
def get_photo(self, obj):
return obj.photo.photo_tag()
get_photo.short_description = 'Photo of prescription'
This is the right answer.we need to use TabularInline.
class RequirementImageInline(admin.TabularInline):
model = RequirementImage
fields = ['image']
extra = 1
class RequirementDocInline(admin.TabularInline):
model = RequirementDoc
fields = ['requirement_file']
extra = 1
class RequirementAdmin(admin.ModelAdmin):
inlines = [RequirementImageInline,RequirementDocInline]
Reference: https://stackoverflow.com/a/74233744/1388835

Automatically search all fields for all models in django admin

I want to be able to search all models for all fields in Django admin, without having to setup ModelAdmin and searchfields individually.
example:
I have all my models in model.py:
# This is an auto-generated Django model module.
from django.db import models
class Diagnosis(models.Model):
id = models.BigAutoField(primary_key=True)
code = models.CharField(max_length=255)
starting_node = models.ForeignKey('Node', models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'diagnosis'
def __str__(self):
return 'Diag #' + str(self.id) + ' - ' + self.code
class DiagnosisHistory(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=255, blank=True, null=True)
date = models.DateTimeField(blank=True, null=True)
id_user = models.TextField(blank=True, null=True)
report = models.TextField(blank=True, null=True)
json_report = models.TextField(blank=True, null=True)
vin = models.CharField(max_length=20, blank=True, null=True)
class Meta:
managed = False
db_table = 'diagnosis_history'
# and so on
and the admin.py where I register the models:
from django.contrib import admin
from . import models
# Do not care. Register everything
for cls in [cls for name, cls in models.__dict__.items() if isinstance(cls, type)]:
admin.site.register(cls)
I don't want to run through each Model and manually create a ModelAdmin with each field
This is the solution I came up with:
from django.contrib import admin
from django.db import models as django_models
from . import models
relationship_fields = (django_models.ManyToManyField, django_models.ForeignKey, django_models.OneToOneField)
for cls in [cls for name, cls in models.__dict__.items() if isinstance(cls, type)]:
meta_fields = [field.name for field in cls._meta.local_fields if not isinstance(field, relationship_fields)]
class Admin(admin.ModelAdmin):
search_fields = meta_fields
admin.site.register(cls, Admin)
Note: registering all fields will fail since some are relationships. using cls._meta.local_fields exclude inferred relationships but you also need to exclude fields such as foreign keys defined in your model. Thus, we filter with isinstance(field, relationship_fields)
Note 2: I should probably use get_fields since local_fields seems to be private API (see https://docs.djangoproject.com/en/3.1/ref/models/meta/)
class MyModelAdmin(admin.ModelAdmin):
# ...
search_fields = [field.name for field in MyModel._meta.fields]
# ...

Django inline model formset with 2 models

First of all, please forgive for my newbie questions. I did copy most of the code, and try to understand from Django documents.
Code as below:
models.py
class Order(models.Model):
ORDER_CHOICES = (
('import', 'IMPORT'),
('export', 'EXPORT')
)
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
order_type = models.CharField(max_length=6, choices=ORDER_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
class Item(models.Model):
def random_barcode():
return str(random.randint(10000000, 99999999))
type = models.ForeignKey(Type, on_delete=models.CASCADE)
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
item_name = models.CharField(max_length=50, help_text='Name of goods, max 50 characters')
barcode = models.CharField(max_length=8, default=random_barcode, unique=True)
production_date = models.DateField()
expired_date = models.DateField()
def __str__(self):
return self.item_type
forms.py
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ['order',]
fields = ['type', 'brand', 'item_name', 'production_date', 'expired_date']
ItemFormSet = inlineformset_factory(Order, Item, form=ItemForm, extra=1)
views.py
class CreatePO(CreateView):
model = Order
context_object_name = 'orders'
template_name = 'storages/create_po.html'
fields = ['order_type', 'storage',]
*#dun't know how to write below code....*
1st question: how to use inline formset to write the CreatePO view?
2nd question: I need my create PO template as below picture, how to add a "Quantity" field?
This kind of template need Javascript, right? Any alternative solution? I have no knowledge with javascript.
First of all, move the def random_barcode(): before def __str__(self): it looks so ugly formated code.
Then let's have a look in your pic, if you haven't proper experience with Javascript you can use Admin Views from Django, it's much more simple and supported by Django 2.1. Read more if you would like to give permission to everyone in a admin-views page https://docs.djangoproject.com/el/2.1/releases/2.1/#model-view-permission
So quantity will be just added inside Item class
quantity = models.PositiveSmallIntegerField(default=1)
Also for your form, in my opinion, you need modelform_factory, so I suggest to read this one https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#modelform-factory-function

Properly using Foreign Key references in search_fields, Django admin

I've got a weird conundrum that I need some help with in Django 1.8.4 using python 3.4 in a virtual-env.
I've got 2 models in 2 different apps... as follows with multiple Foreign Key references.
Inventory App
class InventoryItem(models.Model):
item_unique_code = models.CharField(max_length=256, blank=False, null=False)
category = models.CharField(max_length=256, blank=False, null=False,choices=[('RAW','Raw Material'),('FG','Finished Good'),('PKG','Packaging')])
name = models.CharField(max_length=64, blank=False, null=False)
supplier = models.CharField(max_length=96, blank=False,null=False)
approved_by = models.CharField(max_length=64, editable=False)
date_approved = models.DateTimeField(auto_now_add=True, editable=False)
comments = models.TextField(blank=True, null=True)
def __str__(self):
return "%s | %s | %s" % (self.item_unique_code,self.name,self.supplier)
class Meta:
managed = True
unique_together = (('item_unique_code', 'category', 'name', 'supplier'),)
Recipe App
class RecipeControl(models.Model):
#recipe_name choice field needs to be a query set of all records containing "FG-Finished Goods"
recipe_name = models.ForeignKey(items.InventoryItem, related_name='recipe_name', limit_choices_to={'category': 'FG'})
customer = models.ForeignKey(customers.CustomerProfile, related_name='customer')
ingredient = models.ForeignKey(items.InventoryItem, related_name='ingredient')
min_weight = models.DecimalField(max_digits=16, decimal_places=2, blank=True, null=True)
max_weight = models.DecimalField(max_digits=16, decimal_places=2, blank=True, null=True)
active_recipe = models.BooleanField(default=False)
active_by = models.CharField(max_length=64, editable=False)
revision = models.IntegerField(default=0)
last_updated = models.DateTimeField(auto_now_add=True, editable=False)
def __str__(self):
return "%s" % (self.recipe_name)
class Meta:
managed = True
unique_together = (('recipe_name', 'customer', 'ingredient'),)
I've been getting some weird results in my Recipe's Admin class...
from django.contrib import admin
from django.contrib.auth.models import User
from .models import RecipeControl
from Inventory import models
class RecipeView(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.active_by = request.user.username
obj.save()
fieldsets = [
('Recipe Information', {'fields': ['recipe_name', 'customer']}),
('Ingredients', {'fields': ['ingredient','min_weight','max_weight','active_recipe']}),
('Audit Trail', {'fields': ['active_by','revision','last_updated'],'classes':['collaspe']}),
]
list_select_related = ['recipe_name','customer','ingredient']
search_fields = ['recipe_name','customer','ingredient','active_by']
readonly_fields = ('last_updated','active_by')
list_display = ['recipe_name','customer','ingredient','min_weight','max_weight','last_updated','active_by', 'active_recipe']
admin.site.register(RecipeControl, RecipeView)
The issue I've come across is if I try to do a search on any ForeignKey field, Django throws this error...
Exception Type: TypeError at /admin/Recipe/recipecontrol/
Exception Value: Related Field got invalid lookup: icontains
According to the Django Admin Doc's and other older questions on stackoverflow on the subject it says I should be doing something along the lines of search_fields = ['inventoryitem__name'] but I think this is in reference to FK's in the same app model.py.
Is there a more correct way of referencing/importing other models from other apps that I'm missing or do I have to use some kind of callable method magic to get the search function to look up correctly? I've tried a multitude of different combinations and nothing seems to work. I'm relatively new to Django so I'm confident it's something simple.
You should use the double underscore notation to search a field on a related object. However, you should use the name of the foreign key field (e.g. recipe_name), not the name of the model (e.g. InventoryItem). It doesn't matter whether or not the foreign key's model is in the same app. For example:
search_fields = ['recipe_name__name']
Note that if you want to search the recipe_name and ingredient fields, you need to include both fields, even though they are foreign keys to the same model.
search_fields = ['recipe_name__name', 'ingredient__name']