I'm a little confused as to why this sort of functionality isn't default in the admin, but maybe someone can give me a few hinters to how to go about it.
I have a projects application which keeps track of projects and is to be edited through the admin. Each project has numerous ForeignKey related models (links, flatpages, video, image etc.) that could be placed as inlines within the project admin.
(One or two models have nested inlines, so they don't display in the admin (this and this ticket deal with this) )
Instead of being able to edit these models inline on the project admin (which gets messy and difficult to use), I would love a list of all the current instances of that related model, and simple add/edit button for each model which opens a popup with that model's form.
Project Admin:
- Normal Fields
- Links:
-Link 1 (edit)
-Link 2 (edit)
+ add link <- popup
- Images:
-Image 1 (edit)
-Image 2 (edit)
+ add image <- popup
so on. How would I go about writing this? I only need to do it for one section/model of the admin panel so I don't think writing my own Crud backend is necessary.
Thanks
I implemented something like this in an application once, but since django-admin doesnt support nested inlines (by which i mean inlines within inlines), i followed a slightly different approach. The use case was that you had an invoice (with a few inline attributes) and u had reciepts (again with inline attributes). Reciepts had a foreign key to the invoice model (basically a reciept was part payment of the invoice).
I implemented it by adding a field to the invoice list view which linked to a filtered reciept list view.
So in the invoice admin, there would be:
def admin_view_receipts(self, object):
url = urlresolvers.reverse('admin:invoice_%s_changelist'%'receipt')
params = urllib.urlencode({'invoice__id__exact': object.id})
return 'Receipts' % (url, params)
admin_view_receipts.allow_tags = True
admin_view_receipts.short_description = 'Receipts'
This gives you a link in the list view that takes you to another list view, but filtered by foreignkey. Now you can have inlines for both models and easy access to the related models.
Related
i'm using Wagtail CMS to create product catalogue. I created basic page type for product:
class Product(Page):
It has basic fields like title, description, image aso. But i need "something special":
There is special part available in many variants and each product can have some of them. So I created another model, very simple by:
#register_snippet
class Variant(models.Model):
to store all variants. Variant has name and image. There are about 200 products and 30 variants.
My problem is and I don't know how to manage in Wagtail two tasks:
to link Product with Variants (foreign key) with many-to-many relation to select product related variants in same page as other page entities
each relations has additional parameters (2 params) which are relation specific (material and diameter) and again I haven't found how to display and manage such relations in page editor
I know that Django can handle it by inline formsets (django admin supports it out of box), but is there Wagtail-way to get this done and editable by Wagtail editor? I prefer to manage whole product in the same place, not relations separated in django-admin.
Thanks for any help or advice.
InlinePanel is the Wagtail equivalent of Django admin's inline formsets. An example of this is given in Wagtail's tutorial: https://docs.wagtail.io/en/stable/getting_started/tutorial.html#images
In this case, it's setting up a many-to-many relation between pages and images, with an additional parameter (caption) on the relation; your Product -> Variant relation could be set up in the same way.
In django admin, you can add, edit, and even delete objects from another model if there is a relationship between the two.
For instance, if my code looks like this:
class Category(models.Model):
...
class Product(models.Model):
...
category = models.ForeignKey(Category)
When I am editing/adding a product using the django admin site, in the category field, I have 3 buttons to add/edit/delete categories. Adding one takes to a new window, and once I submit the form, the category is added, the window is closed, and I am returned to my product form with the extra category present. Like this:
How can I do this in my normal application (outside the admin) using forms?
If I understand your question correctly, you could do what django admin does, which is to link the add button to this:
/admin/<your_app>/<your_model>/add/?_to_field=id&_popup=1
and then it uses a bit of javascript to get back the new object you just created. If you look into the contrib/admin/static/admin/js/admin/RelatedObjectLookups.js file (in django's code), you'll see a few functions that pass the id of the calling field to the popup (in showRelatedObjectPopup), and then bring back the selected id (in dismissRelatedLookupPopup).
This is for adding a new object, but you can look into the logic for changing/deleting.
You can replicate that logic with your own forms.
I'd like to create a confirmation page for selected objects before a change is made to them (outside the admin). The objects can be of different models (but only one model a time).
This is much like what is done in administration before deletion. But the admin code is complex and I haven't grasped how it is done there.
First I have severall forms that filter the objects differently and then I pass the queryset to the action / confirmation page. I have created a form factory so that I can define different querysets depending on model (as seen in another similiar question here at Stackoverflow):
def action_factory(queryset):
''' Form factory that returns a form that allows user to change status on commissions (sale, lead or click)
'''
class _ActionForm(forms.Form):
items = forms.ModelMultipleChoiceField(queryset = queryset, widget=forms.HiddenInput())
actions = forms.ChoiceField(choices=(('A', 'Approve'), ('D' ,'Deny'), ('W' ,'Under review'), ('C' ,'Closed')))
return _ActionForm
Which I use in my view:
context['form']=action_factory(queryset)()
The problem is that the items field wont be displayed at all in the html-code when it is hidden. When I remove the HiddenInput widget it displays the form correctly.
I don't want to display the choice field since there can be thousands of objects. All I want to have is something like "Do you want to change the status of 1000 objects" and a popdown and a submit button. A simple enough problem it seems, but I can't get it to work.
If someone has a solution to my current attempt I would be glad to hear how they have done it. Even better would be if there is a cleaner and better solution.
I used the wrong widget. It should be MultipleHiddenInput not HiddenInput.
Consider a wiki application. There is a model Page, that has many Revisions and each revision has many blocks.
What is the simplest way to create an admin in which, you select a page and all the blocks of the latest revision appear; bonus points for letting change of revision by a dropdown (which is by default, sorted in reverse order anyway)
Is it absolutely necessary to create views, or can I extend some of those StackedInline forms, override save and mention some magic meta options, to get it all done automagically.
Have you tried something like this (in admin.py):
class RevInline(admin.TabularInline):
model = Revision
class PageAdmin(admin.ModelAdmin):
model = Page
inlines = (RevInline,)
admin.site.register(Page, PageAdmin)
I'm writing a simple real-estate listing app in Django. Each property needs to have a variable number of images. Images need to have an editable order. And I need to make the admin user-proof.
So that said, what are my options?
Is there a ImageList field that I don't know about?
Is there an app like django.contrib.comments that does the job for me?
If I have to write it myself, how would I go about making the admin-side decent? I'm imagining something a lot slicker than what ImageField provides, with some drag'n'drop for re-ordering. But I'm a complete clutz at writing admin pages =(
Variable lists, also known as a many-to-one relationship, are usually handled by making a separate model for the many and, in that model, using a ForeignKey to the "one".
There isn't an app like this in django.contrib, but there are several external projects you can use, e.g. django-photologue which even has some support for viewing the images in the admin.
The admin site can't be made "user proof", it should only be used by trusted users. Given this, the way to make your admin site decent would be to define a ModelAdmin for your property and then inline the photos (inline documentation).
So, to give you some quick drafts, everything would look something like this:
# models.py
class Property(models.Model):
address = models.TextField()
...
class PropertyImage(models.Model):
property = models.ForeignKey(Property, related_name='images')
image = models.ImageField()
and:
# admin.py
class PropertyImageInline(admin.TabularInline):
model = PropertyImage
extra = 3
class PropertyAdmin(admin.ModelAdmin):
inlines = [ PropertyImageInline, ]
admin.site.register(Property, PropertyAdmin)
The reason for using the related_name argument on the ForeignKey is so your queries will be more readable, e.g. in this case you can do something like this in your view:
property = Property.objects.get(pk=1)
image_list = property.images.all()
EDIT: forgot to mention, you can then implement drag-and-drop ordering in the admin using Simon Willison's snippet Orderable inlines using drag and drop with jQuery UI
Write an Image model that has a ForeignKey to your Property model. Quite probably, you'll have some other fields that belong to the image and not to the Property.
I'm currently making the same thing and I faced the same issue.
After I researched for a while, I decided to use django-imaging. It has a nice Ajax feature, images can be uploaded on the same page as the model Insert page, and can be editable. However, it is lacking support for non-JPEG extension.
There is a package named django-galleryfield. I think it will meet your demand.