Reusing Admin forms for user views in django? - django

Django is making very nice forms after creating a models.py and an admin.py.
How can I reuse these forms (with the extra nice handling of foreign keys and many-to-many fields) in my own views?
ModelForm does only generate "simple" forms. Where do I get the extra batteries?

I was actually able to replicate those green buttons in my forms by following the instructions on this page: http://www.hoboes.com/Mimsy/hacks/replicating-djangos-admin/

A stock ModelForm will do almost all of what the admin does (ForeignKeys will turn into a dropdown select, ManyToManyFields will turn into a multiple-select).
The main exception would be the little green plus buttons for adding a new entry. It would be pretty hard to make those generic, as they depend on a number of admin-specific things: knowing where to find an add page for the linked model; JS to popup a window, close it on submit, and update the parent page; etc. You can dig into the admin and figure out how it implements those extra niceties, but there is not going to be a simple way to drop them into your code.
The other nicety you might be wanting is the filter_horizontal or filter_vertical alternative UIs for a ManyToManyField. Those are implemented as ordinary form widgets, so the potential is there for reusing them in your own code, but I'm guessing it'll take some experimentation and customization to make it work properly.

Related

Can I make Django admin reflect a hierarchy of models?

Assume a Django application with a few models connected by one-to-many relationships:
class Blog(models.Model):
...
class Post(models.Model):
blog = models.ForeignKey(Blog)
...
class Comment(models.Model):
post = models.ForeignKey(Post)
...
Conceptually, they form a hierarchy, a tree-like structure. I want the Django admin to reflect that. In particular:
in a changelist of posts, every post should have a link to the changelist of corresponding comments;
similarly, a post’s edit page should link to the changelist of comments from the top-right buttons area;
when I open that list of related comments, it needs to reflect the relationship in the breadcrumbs (something like: Posts › “Hello world” › Comments) and, ideally, also in the URL (post/123/comment/).
This should of course also apply to the other levels of the hierarchy.
Number 1 is pretty easy with a custom list_display entry and using the ?post__id= query to the comments changelist. But this is little more than a hack. Generally Django assumes my three models to be independent, top-level entities.
Is there a straightforward way to accomplish this? I guess I could override a bunch of templates and AdminModel methods, but perhaps there is a better solution for what seems like a common situation?
Are you sure you are not just looking at Django Admin Inline Models ?
There is no way that an automated admin will pick up your relationships, because in an RDBS there can be any number of foreign keys / one to one / many to many relations, and Django does not have a customized hierarchical behavior built in.
You can indeed edit the breadcrumb customizing an admin template if you want.
For relations you might also be interested into django MPTT that allows to make hierarchical model instances. Also see this question: Creating efficient database queries for hierarchical models (django) in that respect.
How is this a common situation? Consider the fact a model can have a virtually unlimited number of foreign key relationships, let alone visa versa. How would the admin 'know' how to represent this data the way a user requires without customizing things?
One would suggest you are used to work with content management systems rather than webframeworks (no pun intended). It's important to notice Django isn't a cms, but a webframework you can built on top of as you see fit. In a nutshell: 'Django is rather clueless and unaware of contextual requirements'.
Although the admin is quite a beast out-of-the-box, it can be hard to customize. There have been quite some discussions whether it should even be part of core. I can only suggest, if customizing things tends to get hacky, you should probably write your own 'admin', it's not that hard.

Creating Pages for Django Admin add_form Inline Children

I have a node_user which extends auth.User in my django application. The node_user has many children. When creating a new node_user in the django admin I also provide access to node_users's child objects using the StackedInline sublclass.
However, I want to present the node_user children in a series of pages instead of a stacked one-page list. Is there anyway to spread the children out over pages? Does someone have an example? I think this would help user interaction but I'm unsure how to do it because I'm a newbie.
Thanks
angelo
Django doesn't do this out of the box sorry. You could write a custom admin view but you'd have some tricky decisions to make and coding to do regarding saving. I.e. does hitting 'next' lose changes you made on the first page? My approach to this problem would be to edit the node_user children through their own ModelAdmin - smart use of the list_filter and/or search_fields options (and perhaps list_editable) should make it pretty easy to edit them as a group.

Django admin: Change selected box of related fields to autocomplete

We have some models that are have a user as a foreign key. But with about 25000 users in our system, it's a bit daunting to find the one we need.
Is there a solution out there that's better than the select box? Maybe an autocomplete so we can start typing the user name / address? Or just a search box? When switching the related user for these objects, it's getting harder and harder with 25000 unsorted users.
Even just setting it to sort the users by username would be helpful.
I had this problem and my conclusion was to use an autocomplete field instead. It works pretty well in most cases. The only problem is when you have a lot of entries that are mostly the same. For example, in your case, if you type Robert for the name and there's a few hundred Robert entries in the list...
UPDATE
As mentions in shuckc's answer, Django 2.0+ admin as now autocomplete built in.
For older Django or to use outside of the admin (old answer)
There are many apps that add autocomplete to the Django admin:
django-autocomplete-light
django-extensions (ForeignKeyAutocompleteAdmin)
django-autocomplete (on google code)
django-ajax-selects
django-admin-autocomplete
django-autocomplete (tyrion)
My preferred one is the last one. It's well written, it can be used with the admin and outside of the admin, it works with ManyToManyFields, ForeignKeyFields, CharFields, etc.
I did a fork of this project for my client that adds some niceties like a lookup (loupe) button like the ForeignKeyRawIdWidget.
Django 2.0 admin has autocomplete built in, just set the autocomplete_fields field on the ModelAdmin class. e.g.
class QuestionAdmin(admin.ModelAdmin):
ordering = ['date_created']
search_fields = ['question_text']
class ChoiceAdmin(admin.ModelAdmin):
autocomplete_fields = ['question']
The simplest out-of-the-box solution is to add the field to your ModelAdmin's raw_id_fields -- then you'll get a pop-up window in which you can use the built-in searching/filtering and pagination control's to find and select the object you're after.
If you really want autocomplete, the other answers give a you reasonable starting point.
You can use the ForeignKeyRawIdWidget from django.contrib.admin.widgets. It renders FK relations as an input with a small button along-side which presents a searchable pop up.
There is an app for that (django-autocomplete).

Should I modify/extend the admin interface, or write my own CRUD views/templates?

I'm trying to write a simple CRM app in Django; partly as a learning exercise and partly for in-house use.
My schema is slightly complex, as rather that have a single Contact model (with a home phone, work phone, home email, etc.), I have stripped down Cntact model plus a Phone model, an Email model, etc., with a ForeignKey pointing back to a Contact. The point is to let Contacts have an arbitrary number of phone numbers, email addresses, etc. Simple, right?
I have some working views and templates for displaying the data - no issues there. And with only a very small amount of poking at admin.py I have a um...eight different TabularInlines set up, and the admin interface works to create and edit the data...but it's ugly and clunky to the point of unusability, and of course there's no conception of permissions or anything. I'm also not really a fan of having a completely different interface for displaying and searching through the data than for editing and adding contacts...I'd like as much as possible to be done inline, so that I can search for a name, look at the record, click "add note", have it popup a form, fill in the details, click submit, and be done, all with AJAXy goodness so there's no page reloads.
Question: Should I plug away at modifying the admin interface to try and make it usable for a user-facing app? And if so, can anyone point me to a good guide or example where someone has really changed the admin interface to make it work for user-facing CRUD operations?
Or should I just go ahead and write my own CRUD views? And if so, can anyone point me to a good guide or example where someone has written custom CRUD views that work with lots of ForeignKeys and inlines? Ideally I want a form that displays a single Contact, all his Email records, plus a blank form to add a new Email record, plus a button to add more blank forms, plus his Phone records, plus a blank form, and so on for all 8 of my associated models.
(Or am I thinking about this all wrong? Any advice appreciated.)
For our intranet, we use ModelAdmin subclasses (not mounted on the admin site via admin.site.register) for most of our C(R)UD views. By using custom templates for the views, it doesn't look like Django admin at all. What is very convenient though, is that it already handles all the validation/saving for us.
In general, I found admin-"hacking" quite useful to quickly write up C(R)UD views and usually with relatively small changes to your ModelAdmin subclass, you can make it work for your use case.
So I'd vote for use ModelAdmin, but not the one you use in admin, hook a different template and come up with some fancy CSS.
I successfully created a software on top of admin.
The admin hooks (these days) allow very fine-grained customizations, i.e. in general you only need to touch what you want to change.
The changes can go from a trivial cosmetic adjustment to a complete swap-out:
If you provide templates/admin/base.html your admin site can look any way you like. And of course, a navigation bar at the top could include links to some of your own views. Watch out not to hardcode URLs in your links, always reverse.
You can overload ModelAdmin's "change_view", "changelist_view" etc. and swap them for your own views. For example I replaced a default changelist and its simple filtering with a search interface that allows dynamic queries to be built, result columns to be customized by the user, and loading/saving of these searches. That didn't affect any of the other views of that ModelAdmin.
Overloading a ModelAdmin's "get_urls()" let's you rewrap existing admin urls to go to your own views. I did the latter for one model where I wanted the simple Add screen to be replaced by a totally customized Wizard (only leaning on ModelForm).
Don't forget the simplest approach, esp. regarding your "AJAXy goodness": Just define "css" and "js" in your ModelAdmin's Meta. Want to move an inline from the bottom to sit between third and fourth field, and that's not possible via parameters? A one-liner in jquery.
Check out "django-grappelli" for an example of how to improve admin look and feel.
What did you mean by "and of course there's no conception of permissions or anything"?

Django Admin: Need to conditionally display fields

What is the best way to conditionally display a field in the admin depending upon the values of other fields?
In particular I'm thinking about the add_form and change_form. Whenever a certain choice is selected I'd like to hide or disable some fields.
I'm thinking that this might require a javascript solution, but am wondering if there is a better (i.e. builtin) way to do this.
Bernhard is right. You might be able to hack the admin view and template to conditionally show/not show widgets for a field, but if you want to do it dynamically based on user behaviors in the admin, you'll be using javascript.
It's not so terrible, though. At least the Django admin templates have model- and instance-specific ids to give you granular control over your show/hide behavior.
My answer may sound esoteric - but I suspect the design direction you may be taking could benefit from some reconsideration. When an interface requires that much TLC to resort to AJAX to make for a superior UX I am inclined to not use Admin interface for that.
My personal view of the Admin interface of Django is that it is a great freebie that gives me an instant CRUD view at a basic level. I would refrain from piggy backing on it for those who need a more user friendly interface (even if some of these users are handling admin functions). You will acquire technical debt in trying to do these type of Ajax UI/UX and complex form validations using the Admin interface.
I think you should be able to create your own ModelForm and specify that the admin uses that (https://docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form).
Use the _init__() method of the form to selectively display the fields.
I will give it a try and update this answer if it works.
(Actually re-reading the question, it depends. If "values of other fields" is set before the page is loaded, this idea should work. If you want an instant response - click one field and another appears/disappears, yes you will need JavaScript).