=================== Example usage cases =================== Limiting widget queryset ------------------------ Consider a simple, naive example. We want to assign guests to hotel rooms, but for some certain reason we want to have a possibility to assign guests to hotels without assigning them directly to any room. Models ++++++ Because of that, ``Guest`` will have two foreign keys (and a bit of redundancy): .. literalinclude:: ../example/limit_queryset/models.py Widgets +++++++ We want an admin form widget, which will narrow room choices only to those corresponding to currently selected hotel. Before we write the admin and admin form, let's start with widgets: .. literalinclude:: ../example/limit_queryset/widgets.py ``HotelSelect2`` is an instance of original ``django_select2.ModelSelect2Widget`` widget without any modifications. ``RoomSelect2`` is an instance of extended ``linked_select2.LinkedModelSelect2Widget`` widget, which gives access to other (not submitted by the user yet) field values in ``get_queryset`` method. The ``RoomSelect2.get_queryset`` method is passed a form, which will contain a ``hotel`` field. The widget class connects the form field names with corresponding HTML input elements in the frontend and every time an AJAX fetch is happening, the values from these elements are passed as query parameters along with the search query. Both widgets will be used in a form of guest admin. Admin +++++ Here we define the admin: .. literalinclude:: ../example/limit_queryset/admin.py Forms +++++ And here we define the admin form: .. literalinclude:: ../example/limit_queryset/forms.py The ``GuestAdminForm.clean`` method ensures that if room is set, the hotel field must be set aswell and it must be the same exact hotel as the one of selected room. We override default widgets in ``GuestAdminForm.Meta`` class and we pass an argument ``form`` to ``RoomSelect2`` constructor - this form contains a hotel field. All HTML input elements with ids corresponding to field names of this form (only ``hotel`` in this case) will be connected with the room widget. Using generic relations ----------------------- In another naive example, we have two models: ``Item`` and ``Person``. We also have a third model called ``Note``, which has a generic foreign key to these two models, which simply allows to add some annotations to their objects. We are by default able to add notes to people and items through admin inlines, but what if we want to add notes to objects directly through `NoteAdmin`? This short example shows, how to create widgets for two fields: ``content_type`` and ``object_id``, the latter using ``linked_select2.LinkedModelSelect2Widget`` in order to display objects from currently selected content type. The files are going to be shown in a slightly different order than in previous example. Models ++++++ We add a `limit_choices_to` argument to `content_type` field declaration to narrow down the choices to two content types. .. literalinclude:: ../example/generic_relation/models.py Admin +++++ In ``admin.py`` we don't do anything spectacular, but it's important to remebmer to assign `form` attribute to ``NoteAdmin`` in order to override its field widgets. .. literalinclude:: ../example/generic_relation/admin.py Widgets +++++++ .. literalinclude:: ../example/generic_relation/widgets.py ``ContentTypeSelect2`` is a widget using ``django_select2`` class and it simply returns a list of available content types. ``ObjectIdSelect2`` is using ``linked_select2`` class and overrides ``get_queryset`` method in order to select model basing on selected content type and then it returns queryset containing all objects from this model. The method could of course be extended in order to add some extra filtering during the process, but that's not necessary in this case. Forms +++++ The last part of adding our widget is defining the form which will contain them. There are two issues that need to be handled: 1. Making sure that the widget will not return objects from other content types. A malicious user could change selected ``content_type`` value in the frontend in order to get list of objects belonging to other models than ``Item`` or ``Person``. This problem will be handled by filtering the queryset of ``content_type`` in the linked widget form (the ``ObjectIdWidgetForm.limit_content_type_choices`` method). 2. Render the initial value with correct display value when loading the page. The widget has initially no knowledge of selected content type. This problem will be handled by manually assigning the ``object_id`` choices to one-item list containing the object id as value and textual representation of correct model instance with that object id (the ``NoteAdminForm.set_initial_choices`` method). .. literalinclude:: ../example/generic_relation/forms.py