How to filter dropdown choices based on the value of another dropdown in Django Admin? - django-admin

Consider we have the following models:
class Shop(models.Model):
...
class Item(model.Models):
shop = models.ForeignKey('Shop') # Shop where the item is stored.
...
class Order(models.Models):
shop = models.ForeignKey('Shop') # Shop where the ordered item is stored
item = models.ForeignKey('Item') # Ordered item
We are using Django Admin for the creation of orders.
On the "Create Order" page of Django admin, there are 2 dropdowns "Shop" dropdown and "Item" dropdown.
When I choose the shop in the "Shop" dropdown I want the choices in the "Item" dropdown to contain items which are available only in the chosen shop.
How can I implement that?

If you want to filter results based on the value of other fields in form then you can implement this with the help of javascript or Ajax call.
If you don't want write javascript or ajax code then you can use django-autocomplete-light package.
you create a form and forward selected value of shop for item field in the form and access in the view where you can set queryset for item form field. You can also search in item field value.
In forms.py,
from django import forms
from dal import autocomplete
class OrderForm(forms.ModelForm):
item = forms.ModelChoiceField(
queryset=Item.objects.all(),
widget=autocomplete.ModelSelect2(
url='item_autocomplete',
forward=['shop']
),
)
class Meta:
model = Order
fields = "__all__"
In urls.py,
urlpatterns = [
path('item_autocomplete/', ItemAutocompleteView.as_view(), name='item_autocomplete'),
]
In views.py,
from dal import autocomplete
from .models import Item
class ItemAutocompleteView(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return Item.objects.none()
shop = self.forwarded.get('shop', None)
if shop:
qs = Item.objects.filter(shop=shop)
else:
qs = Item.objects.none()
# I assume your Item model has `title` field (this is searchable column. you can search on field value )
if self.q:
qs = qs.filter(title__istartswith=self.q)
return qs
In admin.py,
#admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
form = OrderForm
...
forward value doc link- https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#filtering-results-based-on-the-value-of-other-fields-in-the-form

Related

How to add a popup in Django admin to link back to the current inline record

I have a group of models something like:
class Parent(models.Model):
name = models.CharField()
class Child(models.Model):
name = models.CharField()
parent = models.ForeignKey(Parent)
class GrandChild(models.Model):
name = models.CharField()
parent = models.ForeignKey(Child)
Within Django admin I can set the Child as an admin.TabularInline for Parent. I want to add a link which will pop a window allowing me to Add a GrandChild
At the moment I can create a list of existing GrandChild records by including the following within my admin.TabularInline for the ChildInline
def grand_children(self, obj):
text = ''
for grandchild in obj.grandchild_set.filter(completed=False):
text += grandchild.name + '<br />'
return mark_safe(text)
grand_children.short_description = 'Grand Children'
I would like to add something that pops a Django admin popup (like it does on a ForeignKey relationship) to add a GrandChild linked to the associated Child.
I don't know how to pop the window with the Child id set. Can you help?

ActiveAdmin/Formtastic custom input (JSON)

I have a json column in my database (actually jsonb, but that doesn't matter here) table items of only plain
{
"key1":"value1",
"key2":"value2"
}
structure. No nesting.
The "keys" are actually foreign key-like integers pointing at properties table, which looks like
id | name
-----------
1 | Height
2 | Weight
3 | Color
etc.
I'm using ActiveAdmin for administration and want to have a custom input on editing an Item composed of a select input and a text field, which will allow me to:
select a property from the properties table OR input a new property, and then
type a JSON into the text field.
Can't figure out how though.
What have I tried and what my problems are?
I have created a custom input in app/inputs/property_input.rb:
class PropertyInput
include Formtastic::Inputs::Base
include Formtastic::Inputs::Base::Collections
def to_html
input_wrapping do
label_html <<
select_html <<
text_html
end
end
protected
def select_html
builder.select(input_name, collection, input_options, input_html_options)
end
include Formtastic::Inputs::Base::Stringish
include Formtastic::Inputs::Base::Placeholder
def text_html
builder.text_field(input_name)
end
end
Calling it as below from admin/item.rb
f.input :property, as: :property, collection: ItemAttribute.all
My problem is:
Right now, the select shows correctly the name of the property, but the text field shows just id of the same property. How to decouple these two fields and force the text field to save and read from the Item jsonb column?
Maybe it is important to say that I do not use ActiveRecord associations between Item and Property models in this case as I'm not sure if it even works with the jsonb column holding "foreign keys".

How to iterate over object list in reverse order in Grails?

From a list of GORM database objects that are ordered desc in my user domain model I want to order them asc (i.e., in reverse order in the view). For example to get the latest books of a user from the database, but insert them in the Dom in reverse order where the latest book comes last.
How do I perform a reverse each in my GSP?
Controller:
def books = user.books
GSP:
<g:each in="${books}" var="book">${book}</g:each>
You can use default sort for relation collection as described here. So if you define like this:
class User {
…
static hasMany = [books: Book]
static mapping = {
books sort: 'publishDate', order: 'asc'
}
}
The collection will be sorted on database level
<g:each in="${books.reverse()}" var="book">${book}</g:each>
EDIT
Got carried away :). I would rather suggest:
def books = user.books?.reverse() in controller.
(Separation of concern, view should not have logic of manipulating model)
UPDATE:
In case books are not ordered in User, explicit sorting is required.
def newestBooks = user.books?.asList().sort{it.publishDate}
to reverse sort use
def newestBooks = user.books?.asList().sort{-it.publishDate}

Calling and passing parameters from view to controller w/ select box and button

I'm trying to order a table (without any plugin) in a Rails app. The idea is to retrieve a table from database and order it with 3 select box. After I click on the "Order" button and refresh page with ordered data.
I'm trying something and get this code:
def order
#clients = Client.all(:order => 'parameter1, parameter2, parameter 3')
end
but, I have two questions:
How can I pass parameters from a <select> box to controller's method order?
How can I call this method and refresh the page with ordered data when I click the button?
I think the same controller action that renders the table now could be extended to handle the incoming sort options. For example, it might be the current index method in your controller.
So build a form within your current page using form_for, with select helpers and a submit button that re-calls index.
Within the controller, you can access the values selected in the select boxes using the params hash that Rails sets for you. For example, if your sorting parameters were "height", "age", and "gender", in select lists sort1, sort2 and sort3 you might have code that does something like
def index
sort_list = []
# assumes the value of the sort list is the column name
sort_list << params[:sort1] if params[:sort1]
sort_list << params[:sort2] if params[:sort2]
sort_list << params[:sort3] if params[:sort3]
if sort_list.blank?
#clients = Client.all
else
#clients = Client.all(:order => sort_list.join(", "))
end
...
end
I know you said you didn't want a plugin (gem), but I have to mention a great one that rocks for even pretty complicated sorting (and filtering) called ransack.

Help with Creating Models for Views

I am trying to create a Model to pass to a gsp view. I would like to do a sub query across two tables. I have two domains, alum_profile and alum_position. alum_profile has many alum_position's. alum_position belongs to alum_profile. In SQL if I wanted to create a result set, I would have something like this:
Select count(id),
(Select CONCAT(first_name, ' ', last_name)
From alum_profile
where
alum_profile_id =alum_profile.id ) as Person
FROM alum_position
GROUP BY alum_profile_id
ORDER BY count(id) DESC
How do I do this with HQL and create a model that can be passed to a gsp View.
Thanks for your help
jason
I am using Spring Source, with MySQL and writing in groovy on grails
From what I've read of your question, you want to display a list of the Profile's names, along with how many Positions each Profile has, sorted by the number of positions, desc.
First, you need Models:
class AlumProfile {
String first_name
String last_name
def hasMany = [positions: AlumPosition]
};
class AlumPosition {
String name // I just added this, no idea what you need in here
def belongsTo=AlumProfile
};
Now you want to create a list of the AlumProfiles sorted by position count. In your controller, you need:
def allByPositionCount = {
def profiles = AlumProfile.list().sort( [compare: { a,b -> a.positions.size().compareTo( b.positions.size() ) }] as Comparator );
[ profiles: profiles ]
}
This will render the allByPositionCount.gsp with the model containing the "profiles" member that is the list of profiles in the correct order, so something like:
<g:each in="${profiles}" var="profile" >
${profile.first_name} ${profile.last_name} has ${profiles.positions.size()} positions
</g:each>
should render what you want.

Resources