I would like to be able add multiple records at a time in django admin.
models.py
class Photo(models.Model):
pub_date = models.DateTimeField('date published')
sort = models.IntegerField()
photo_category = models.ForeignKey(Photocategory)
title = models.CharField(max_length=200)
title_url = models.SlugField(max_length=200)
main_image = models.ImageField(blank=True, null=True, upload_to='images/photo/')
# size is "width x height"
cropping = ImageRatioField('main_image', '350x350')
def image_thumbnail(self):
return '<img width="160px" src="/media/%s"/>' % (self.main_image, self.main_image)
image_thumbnail.allow_tags = True
image_thumbnail.short_description = 'main_image'
def __unicode__(self):
return self.title
admin.py
class PhotoAdmin(ImageCroppingMixin, admin.ModelAdmin):
prepopulated_fields = {'title_url': ('title',)}
list_display = ('title', 'photo_category', 'image_thumbnail', 'sort')
list_filter = ['photo_category']
admin.site.register(Photo, PhotoAdmin)
Screenshot:
Is there a way I could do lets say 5 at a time on the 1 screen, it is very slow filling that out 1 by 1. I can do it in mysql with SQL query but I would like to achieve this here.
Thanks
Hey Yes this is possible but Out of the Box,,
Here is what you will have to do:
Define a Formset (to create multiple photo forms) in form Attribute of PhotoAdmin
You will see a formset on the add photo Admin page, and should work correctly. If you don't then you can over-ride the templates by providing add_form_template and change_form_template attributes in Admin and display that form-set templates.
Handle Saving of Multiple objects in the Formset
For example:
# admin.py
#admin.register(Photo)
class PhotoAdmin(ImageCroppingMixin, admin.ModelAdmin):
prepopulated_fields = {'title_url': ('title',)}
list_display = ('title', 'photo_category', 'image_thumbnail', 'sort')
list_filter = ['photo_category']
form = MyFormset
add_form_template = "photo/admin/my_add_form.html"
change_form_template = "photo/admin/my_change_form.html"
This is a rough workflow, you will have to try this and make modification if required.
You can refer to this documentation: https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#modeladmin-options
Related
The frontend of my Django site is in Persian language which is RTL and everything is ok except that the CharField model fields are in LTR direction when edited in the Admin site.
Here's my model:
class Post(models.Model):
STATUS_CHOICES = (('draft', 'Draft'), ('published', 'Published'))
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=100, allow_unicode=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
lead = RichTextField()
body = RichTextUploadingField()
created_on = models.DateTimeField(auto_now_add=True)
published_on = models.DateTimeField(default=timezone.now)
updated_on = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
is_featured = models.BooleanField(default=False, verbose_name='Featured Post')
objects = models.Manager()
published = PublishedManager()
featured = FeaturedManager()
class Meta:
ordering = ('-published_on',)
def __str__(self):
return self.title
I know I can set the site's language to Persian and solve this issue but I don't want to because the Persian translation of Django is dull.
Another solution is to use one of available Rich Text editors (tinymce or ckeditor) but those are overkill for a CharField field.
I also tried custom admin form like this:
class PostAdminForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'author', 'lead', 'body', 'status', 'is_featured']
widgets = {'title': forms.TextInput(attrs={'dir': 'rtl'})}
#admin.register(Post, PostAdminForm)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'created_on', 'published_on', 'status', 'is_featured')
list_filter = ('status', 'created_on', 'published_on', 'is_featured')
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',)
date_hierarchy = 'published_on'
ordering = ('status', 'created_on', 'published_on')
But it gives me this error:
AttributeError: 'ModelFormOptions' object has no attribute 'abstract'
In your admin.py file for your app, you can create a custom form for your model. I don't know your model so I will use general names for this as an example:
from django.contrib import admin
from app_name.models import *
from django import forms
class CustomAdminForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['mycharfield']
widgets = {'mycharfield':forms.TextInput(attrs={'dir':'rtl'})}
admin.site.register(MyModel,CustomAdminForm)
This should make the mycharfield text input render as being RTL on the admin page for the MyModel form. The line widgets = {'mycharfield':forms.TextInput(attrs={'dir':'rtl'})} will change the text input widget's dir attribute to the rtl value. If you have more than one CharField in your model and want RTL for all of them simply add each field to the fields attribute in the form and do the same thing with the widget attribute for each field.
As the answer provided by Nathan was partially correct, I did my own research and found that the correct way is this:
class CustomPostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'lead', 'body']
widgets = {'title': forms.TextInput(attrs={'dir': 'rtl', 'class': 'vTextField'})}
and then:
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
form = CustomPostForm
What surprises me is that the above code removed vTextField class from the input field so I had to add it again.
I'm trying to build a filter that corresponds to the has_images method on my Django admin, but I can't because it strictly says that has_images is not a field of the model. I tried setting it up as a property, but it also didn't work.
I thought about defining has_images as a field and really calculating it, based on the changes on the model, but I think that would be not optimal.
What would be a good solution here?
models.py
class Product(models.Model):
name = models.CharField("Name", max_length=255)
def has_images(self):
return self.images.all().count() > 0
has_images.boolean = True
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="images")
file = models.ImageField("Product Image")
admin.py
class ProductImageInline(admin.TabularInline):
model = ProductImage
fields = ('file',)
extra = 1
class ProductAdmin(VersionAdmin):
list_display = ('id', 'name', 'has_images',)
inlines = (ProductImageInline,)
Expected result:
Can you share the contents of the admin.py file?
Or let me explain it as follows. Add a feature called list_filter = ('images') into the ProductAdmin class you created in admin.py. If this feature doesn't work (I'm not sure as I haven't tried it), if you create an Admin Class for ProductImages directly, you can already view the pictures and the corresponding Product on that page.
----------- EDIT ----------------
This is how I solved the problem.
models.py
from django.db import models
class Product(models.Model):
name = models.CharField("Name", max_length=255)
is_image = models.BooleanField(default=False, editable=False)
def save(self, *args, **kwargs):
if self.images.count():
self.is_image = True
else:
self.is_image = False
super(Product, self).save(*args, **kwargs)
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="images")
file = models.ImageField("Product Image")
def save(self, *args, **kwargs):
super(ProductImage, self).save(*args,**kwargs)
self.product.save()
admin.py
from django.contrib import admin
from .models import *
class ProductImageInline(admin.TabularInline):
model = ProductImage
fields = ('file',)
extra = 1
class ProductAdmin(admin.ModelAdmin):
list_display = ('id', 'name',)
list_filter = ('is_image',)
inlines = (ProductImageInline,)
admin.site.register(Product, ProductAdmin)
Here I added an is_image BooleanField field with False by default. Every time the save method of the Product model runs, it checks whether there is an image in the ProductImage to which the Product model is attached. If there is an image in it, is_image is set as True.
My Goal:
When I add Inlines in admin, to add that Model Object, only one Inline Item with a particular Foreign Key should be allowed.
My Models:
class RateCard(models.Model):
name = models.CharField(max_length=100)
year = models.IntegerField(choices=YEAR_CHOICES, default=timezone.now().year)
class BillingCategory(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "Billing Categories"
class BillingCatagoryRate(models.Model):
billing_category = models.ForeignKey(BillingCategory, on_delete=models.CASCADE)
billing_rate = models.PositiveIntegerField()
rate_card = models.ForeignKey(RateCard,
on_delete=models.CASCADE, blank=True, null=True)
Refer the admin screenshot.
I can select the Drop Down with same value more than once and save successfully. What I need is, RateCard should have only one BillingCatagory used once . NOT multiple (like shown in the picture)
My Ideas:
During Save, may be we can check if there are any duplicates in Billing Categories.
This is how I finally solved it . I added a check in admin.py for the ModelAdmin class.
We need to override the "save_formset" method to access the inline values . This is how my RateCardAdmin looks.
class RateCardAdmin(admin.ModelAdmin):
model = RateCard
list_display = ('name', 'year')
inlines = [BillingCatagoryRateInline]
def save_formset(self, request, form, formset, change):
ids = []
for data in formset.cleaned_data:
ids.append(data['billing_category'])
common_ids = [item for item , count in collections.Counter(ids).items() if count > 1]
if common_ids:
raise ValidationError('Billing Category Duplicate found')
formset.save()
I am writing a blog app using django 1.10
This is a snippet of my object model:
model.py
class Attachment(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(upload_to='attachments')
class FileAttachments(models.Model):
title = models.CharField(max_length=50)
attachments = models.ManyToManyField(Attachment)
class Post(models.Model):
title = models.CharField(max_length=200)
text = models.CharField(max_length=2000)
file_attachments = models.ForeignKey(FileAttachments, blank=True, null=True)
slug = models.SlugField(max_length=40, default='', unique=True)
author = models.ForeignKey(User, default=1)
pub_date = models.DateTimeField(blank=True, null=True)
def get_absolute_url(self):
return "/blog/%s/%s/%s/%s/" % (self.pub_date.year, self.pub_date.month, self.pub_date.day, self.slug)
def __unicode__(self):
return self.title
class Meta:
verbose_name = "Blog Post"
verbose_name_plural = "Blog Posts"
ordering = ["-create_date"]
permissions = (
( "create", "Create Post" ),
( "modify", "Modify Post" ),
( "delete", "Delete Post" ),
)
(simplified) admin.py:
class PostAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
exclude = ('author',)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
# Register your models here.
admin.site.register(Post, PostAdmin)
when I try to access the Post object via the admin page - in the list view, I only see 'Post object' - whereas I want to see the title of the post (and possibly, a few other attributes of the Post object) - how do I modify the admin view to achieve this?
For your first problem - you need to define list_display in your PostAdmin, like that:
class PostAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
exclude = ('author',)
list_display = ('title', 'pub_date')
As for your second problem - please stick to the 'One post, one problem' rule; hint - ForeignKey means only one FileAttachment can be related to your Post.
Change __unicode__ for __str__ in your Post class and print whatever you want. For example: return "Blog %s published on %s" %(self.title, self.pub_date).
Your Post model only includes one attachment through a foreign key. That way it will be impossible to upload more than one file. In other words, you have to change your models, for example including a key in FileAttachments relating to a Post and removing the key from Post model.
Hope this helps.
I'm creating simple blog and face with problem. I need to have separate type of posts in Django admin page which would be saved, but not showed on site. For example, when somebody suggest post, I want at first read it and after that publish, or when I'm writing a post and want go on after some time I need to save it.
blog/models.py
class Post(models.Model):
author = models.ForeignKey(User, default=1)
title = models.CharField(max_length = 50)
pub_date = models.DateTimeField(default=timezone.now)
content = models.TextField(max_length = 50000)
published = models.BooleanField(default=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return "/blog/%i/" % self.pk
blog/admin.py
class PostAdmin(admin.ModelAdmin):
fieldsets = (
('Title', {'fields' : ['title']}),
('Date', {'fields' : ['pub_date'], 'classes' : ['collapse']}),
('Content', {'fields' : ['content']}),
)
list_display = ('title', 'pub_date')
list_filter = ['pub_date']
search_fields = ['title']
admin.site.register(Post, PostAdmin)
blog/views.py
class PostsListView(ListView):
model = Post
You can modify your list view to only show published posts by overriding get_queryset.
class PostsListView(ListView):
model = Post
def get_queryset(self):
return super(PostsListView, self).get_queryset().filter(published=True)
If you have a detail view, you should override get_queryset in the same way.
In your model admin, you can add published to list_filter. This makes it easy to filter published/unpublished posts.
class PostAdmin(admin.ModelAdmin):
...
list_filter = ['pub_date', 'published']