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

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?

Related

How to filter dropdown choices based on the value of another dropdown in 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

How to add attributes that are not present in the model to the object in rails?

I have a requirement where I would like to add a attribute to the object so as to identify the type it belongs
dresses = Cloth.select("id as id and cloth_name as name")
and I want to add cloth_type along with each resulting object and this cloth_type is not actually present in my model but I need to add this. So how can I do this?
say for example
dresses.cloth_type = "jeans"
and the result should be something like
{"id" : 1, "name" : "dress1", "cloth_type" : "jeans"}
I actually tried assign_attributes, write_attributes and update and none seems to work.
Please help. Thanks in advance.
You can simply add an attr_accessor to your model:
class Cloth < ActiveRecord::Base
attr_accessor :cloth_type
end
c = Cloth.first
c.cloth_type = "whatever"
c.cloth_type # => "whatever"
You can also add the attribute to all records (it will not be an attribute in the traditional sense) using the select method.
clothes = Cloth.select("*, 'whatever' as cloth_type")
clothes.first.cloth_type # => "whatever"
If you wanna deal with several types of objects keeping only one table in database (here clothes for objects dresses or many others), you can use STI. It will allow you to have several objects if u need to implement different behaviors for each.
If you don't, you can just create a migration to add a column named 'cloth_type' in your table and then put this in your class :
scope :dresses, -> { where(cloth_type: 'dress') }
Then you should be able to access Clothes.dresses.
Or you can just do :
dresses = Cloth.where(cloth_type: 'dress')

Grails result set pagination

Suppose I have a class User with many Customers (marked with hasMany property).
In the class Customer I mark the owner with belongsTo.
Now when I want to get user's customers I simply write User.read(params.id).customers
I want a pagination in my test project, so reading the entire customer list doesnt make much sense.. I'd use something like Customer.findAllByOwner but this method is such a method is not present..
How do I limit the result set of such a query then (offset and limit)?
package myapp
class User {
...
static hasMany = [customers: Customer]
...
}
package myapp
class Customer {
...
static belongsTo = User
...
}
Correct me if I'm wrong, but the idea in its most simplistic form here is that you want to get a list of Customers based off of the User object.
In that case, change your Customer domain class to something like...
class Customer {
...
static belongsTo = [user: User]
...
}
Then, in your controller you can do something like:
def customerInstanceList = Customer.findAllByUser(User.get(params.id), [max: params.max, offset: params.offset])
Hope this helps.
The name of the property in the Customer that you are trying to access is 'user' instead of 'owner'. If you want the property name to be owner it should be:
static belongsTo = [owner : User]
You can paginate the result set of a dynamic finder by supplying a map containing the pagination parameters as the final parameter.
def customers = Customer.findAllByOwner(user, [max:params.max,
offset:params.offset)

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.

Rails validation for a has_many association

I am having trouble with validations on a has_many relationship where the children exist, but the parent doesn't. However, when creating/saving the parent object, I want to ensure that specific children (with certain attributes) have already been saved.
There is a Parent object that has_many Child objects. The Child objects are persisted into the database first, and thus don't have any reference to the parent. The association structure is:
Parent
- has_many :children
Child
- someProperty: string
- belongs_to: parent
For example, there are three child objects:
#1 {someProperty: "bookmark", parent: nil}
#2 {someProperty: "history", parent: nil }
#2 {someProperty: "window", parent: nil }
A parent is valid only if it contains child objects with someProperty history and window.
I am setting up the parent inside the controller as:
p = Parent.new(params[:data])
for type in %w[bookmark_id history_id window_id]
if !params[type].blank?
p.children << Child.find(params[type])
end
end
// save the parent object p now
p.save!
When the children are assigned to the parent with <<, they are not saved immediately as the parent's id does not exist. And for the parent to be saved, it must have at least those 2 children. How could I solve this problem? Any input is welcome.
Not sure why you need to do such a thing, but anyway, how about doing this?
class Parent < ActiveRecord::Base
CHILDREN_TYPES = %w[bookmark_id history_id window_id]
CHILDREN_TYPES.each{ |c| attr_accessor c }
has_many :children
before_validation :assign_children
validate :ensure_has_proper_children
private
def assign_children
CHILDREN_TYPES.each do |t|
children << Child.find(send(t)) unless send(t).blank?
end
end
def ensure_has_proper_children
# Test if the potential children meet the criteria and add errors to :base if they don't
end
end
Controller:
...
p = Parent.new(params[:data])
p.save!
...
As you can see, I moved all the logic to model at the first place. Then, there is a two-step process for saving children. First, we assign children to the parent and then we validate if they meet the required criteria (insert your logic there).
Sorry for being short. I'll answer any further questions if necessary.
First thing, if you want the children to be saved without the parent id then there is no point in doing this
p = Parent.new(params[:data])
for type in %w[bookmark_id history_id window_id]
if !params[type].blank?
p.children << Child.find(params[type])
end
end
the whole purpose of
p.children << some_child
is to attach the parent id to the child object which you are not doing here because the parent doesn't exist yet.
The other thing is if you just want to make sure that the parent has a child object and if you are creating child and parent together then you can use transaction block around the parent and child creation which will make sure that the parent has child, like
transaction do
p = create_parent
p.children << child1
p.children << child2
end
So, within the transaction, if at any stage code fails then it will rollback the whole db transaction , i.e you will either have one parent with 2 children or nothing, if that's the end state you are looking for.
EDIT: Since you can't create a parent unless it has 2 children, in that case, instead of
p = Parent.new(params[:data])
for type in %w[bookmark_id history_id window_id]
if !params[type].blank?
p.children << Child.find(params[type])
end
end
do
children = []
for type in %w[bookmark_id history_id window_id]
if !params[type].blank?
children << Child.find(params[type])
end
end
if children.size >= 2
p = Parent.create!(params[:data])
children.each {|child| p.children << child}
end
Does that make sense

Resources