Radio Button on Many-to-Many field using Inline ModelAdmin - python

Heres the scenario :
class Account(model.Model):
acc_name = models.CharField(max_length=50)
class Person(model.Model):
accounts = models.ManyToManyField(Account)
class Message(model.Model):
person = models.ForeignKey(Person)
msg = models.CharField(max_length=500)
Now I am using InineModelAdmin in my admin.py. So it looks like this :
class Account(admin.ModelAdmin):
...
some code
...
admin.site.register(Account, AccountAdmin)
class MessageInLine(admin.StackedInline):
model = Message
class PersonAdmin(admin.Modeladmin):
inlines = [MessageInLine]
admin.site.register(Person, PersonAdmin)
So it stands that Message has a ForeignKey on Person and Person has a Many-To-Many with Account.
Now in the Django-admin, where I add Person it obviously gives the components of Person and Message. Now here the accounts are in a list, where they need to be selected by holding CTRL. I want to use a radio_button to allow selecting multiple buttons for faster selection. How can I do this?
Edit :
I tried using radio_field like this inside PersonAdmin :
radio_fields = {"accounts":admin.VERTICAL}
But it gives me an error that says that it is nether a ForeignKey nor does it have a Choices Set So obviously this isnt working. Is there a way around this?

From this Answer seems there is a way using CheckboxSelectMultiple
from django.forms.widgets import CheckboxSelectMultiple
class PersonAdmin(admin.ModelAdmin):
inlines = [MessageInLine]
formfield_overrides = {
models.ManyToManyField: {'widget': CheckboxSelectMultiple},
}
Please read the another question too

Related

Django Admin TabularInline - Getting Foreign Key through a OneToOne Relationship

As shown below, I want to display one model (Attendance) in the admin change view of another (Event) as a TabularInline. However, Attendance doesn't have a direct ForeignKey pointing to Event. Rather, it has a OneToOne relationship with Registration, which in turn points to Event. Any simple ways of achieving this?
As you can see from the code, I tried to access the Event using a method, and pass it into the TabularInline class as fk_name, but it threw an error saying "'myapp.Attendance' has no field named 'get_fk'".
myapp/models.py
from django.db import models
class Event(models.Model):
...
class Registration(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
...
class Attendance(models.Model):
registration = models.OneToOneField(Registration, primary_key=True, one_delete=models.CASCADE)
...
def get_fk(self):
return self.registration.event
myapp/admin.py
from django.contrib import admin
class AttendanceInline(admin.TabularInline):
model = Attendance
fk_name = 'get_fk'
class EventAdmin(admin.ModelAdmin):
inlines = [AttendanceInline]
My current workaround is to add another 'event' ForeignKey to Attendance, but it seems redundant as the same data is already stored inside 'registration'.
Appreciate it if anyone could help!
fk_name should be field name that have Foreignkey relation according to documentation not any property of model.
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.fk_name

How to create a multiplechoice form with models queryset?

The goal of this application is to let users select their favourite fonts using checkboxes and use the selected fonts somewhere else.
The thing is that, all the fonts are entered through the Admin panel by the admin, so the form should be able to get it from there.
This is what I thought doing :
forms.py
class ContactForm1(forms.ModelForm):
choice = forms.MultipleChoiceField(choices=""" Get my models title as choice """, widget=forms.CheckboxSelectMultiple())
class Meta:
model = ImageCheckView
fields = ['title', 'choice']
...
models.py
class ImageCheckView(models.Model):
title = models.CharField(max_length=100, unique=True)
...
What it will have to look like :
(I want to keep the selected options so I can use it somewhere else.)
How can I achieve the following ?
Found a solution, I can create a new checkbox field on my forms and pass the object choices like this :
checkbox = forms.MultipleChoiceField(ImageCheckView.objects.all(), widget=forms.CheckboxSelectMultiple)

Django model select choice, populated with fields from other model instances

Suppose I have a model Car that has a field brand and a model owner that has two field: name and car_brand, whereas the latter should be one of the Car instances brands. I need to have a form where the user is presented with a text field name and a drop down select choice populated with all the brand name of Car instances. How would I achieve that?
Here is the code I am starting with. Feel free to correct. Thanks.
models.py
from django.db import models
class Car(models.Model):
brand = models.CharField(max_length=20)
class Owner(models.Model):
name = models.CharField(max_length=20)
car_brand = models.ForeignKey(Car)
forms.py
from django.forms import ModelForm, TextInput, Select
from app.models import Owner
class OwnerForm(ModelForm):
class Meta():
model = Owner
fields = ("name", "car_brand")
widgets = {
"name" : TextInput(attrs={"class" : "name"}),
"car_brand" : Select(attrs={"class" : "car_brand"}),
}
You could probably just define a __unicode__ method on your Car model and I think it should work fine. As Daniel mentioned, the form might be getting confused since you've overridden the widgets. I could be wrong, but I thought django automatically rendered attributes on form elements that can be used for styling purposes. Maybe you don't need to override the widgets. If not, you can specify the form field explicitly:
class OwnerForm(ModelForm):
car_brand = forms.ModelChoiceField(
queryset=Car.objects.all(),
widget=Select(attrs={'class': 'car_brand'}),
)
class Meta:
model = Owner
fields = ('name', 'car_brand')
widgets = {
'name': TextInput(attrs={'class': 'name'})
}
As a side note, is there any reason you don't have a CarBrand model and a foreign key field relating the Car model to it? That would be a more normalized approach to modeling your data.

In Django Admin, I want to change how foreign keys are displayed in a Many-Many Relationship admin widget

I have a ManyToMany relationship:
class Book:
title = models.CharField(...)
isbn = models.CharField(...)
def unicode(self):
return self.title
def ISBN(self):
return self.isbn
class Author:
name = models.CharField(...)
books = models.ManyToManyField(Book...)
In the admin interface for Author I get a multiple select list that uses the unicode display for books. I want to change the list in two ways:
1) Only for the admin interface I want to display the ISBN number, everywhere else I just print out a "Book" object I want the title displayed.
2) How could I use a better widget than MultipleSelectList for the ManyToMany. How could I specify to use a CheckBoxSelectList instead?
To display the ISBN you could make a custom field like this:
class BooksField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return obj.isbn
There's a CheckboxSelectMultiple for the ManyToManyField but it doesn't display correctly on the admin, so you could also write some css to fix that.
You need to create a form for the model, and use that in your admin class:
class AuthorForm(forms.ModelForm):
books = BooksField(Book.objects.all(), widget=forms.CheckboxSelectMultiple)
class Meta:
model = Author
class Media:
css = {
'all': ('booksfield.css',)
}
class AuthorAdmin(admin.ModelAdmin):
form = AuthorForm
For 2), use this in your AuthorAdmin class:
raw_id_fields = ['books']
Check here: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#ref-contrib-admin for instructions on creating a custom ModelAdmin class. I've thought about this a lot myself for my own Django project, and I think 1) would require modifying the admin template for viewing Author objects.

Django Admin: OneToOne Relation as an Inline?

I'm putting together the admin for a satchmo application. Satchmo uses OneToOne relations to extend the base Product model, and I'd like to edit it all on one page.
Is it possible to have a OneToOne relation as an Inline? If not, what is the best way to add a few fields to a given page of my admin that will eventually be saved into the OneToOne relation?
for example:
class Product(models.Model):
name = models.CharField(max_length=100)
...
class MyProduct(models.Model):
product = models.OneToOne(Product)
...
I tried this for my admin but it does not work, and seems to expect a Foreign Key:
class ProductInline(admin.StackedInline):
model = Product
fields = ('name',)
class MyProductAdmin(admin.ModelAdmin):
inlines = (AlbumProductInline,)
admin.site.register(MyProduct, MyProductAdmin)
Which throws this error: <class 'satchmo.product.models.Product'> has no ForeignKey to <class 'my_app.models.MyProduct'>
Is the only way to do this a Custom Form?
edit: Just tried the following code to add the fields directly... also does not work:
class AlbumAdmin(admin.ModelAdmin):
fields = ('product__name',)
It's perfectly possible to use an inline for a OneToOne relationship. However, the actual field defining the relationship has to be on the inline model, not the parent one - in just the same way as for a ForeignKey. Switch it over and it will work.
Edit after comment: you say the parent model is already registered with the admin: then unregister it and re-register.
from original.satchmo.admin import ProductAdmin
class MyProductInline(admin.StackedInline):
model = MyProduct
class ExtendedProductAdmin(ProductAdmin):
inlines = ProductAdmin.inlines + (MyProductInline,)
admin.site.unregister(Product)
admin.site.register(Product, ExtendedProductAdmin)
Update 2020 (Django 3.1.1)
This method is still working but some types has changed in new Django version since inlines in ExtendedProductAdmin should now be added as list and not tuple, like this:
class ExtendedProductAdmin(ProductAdmin):
inlines = ProductAdmin.inlines + [MyProductInline]
Or you will get this error:
inlines = ProductAdmin.inlines + (MyProductInline,)
TypeError: can only concatenate list (not "tuple") to list
Maybe use inheritance instead OneToOne relationship
class Product(models.Model):
name = models.CharField(max_length=100)
...
class MyProduct(Product):
.....
Or use proxy classes
class ProductProxy(Product)
class Meta:
proxy = True
in admin.py
class MyProductInlines(admin.StackedInline):
model = MyProduct
class MyProductAdmin(admin.ModelAdmin):
inlines = [MyProductInlines]
def queryset(self, request):
qs = super(MyProductAdmin, self).queryset(request)
qs = qs.exclude(relatedNameForYourProduct__isnone=True)
return qs
admin.site.register(ProductProxy, MyProductAdmin)
In this variant your product will be in inline.
Referring to the last question, what would be the best solution for multiple sub-types. E.g class Product with sub-type class Book and sub-type class CD. The way shown here you would have to edit a product the general items plus the sub-type items for book AND the sub-type items for CD. So even if you only want to add a book you also get the fields for CD. If you add a sub-type e.g. DVD, you get three sub-type field groups, while you actually only want one sub-type group, in the mentioned example: books.
You can also try setting 'parent_link=True' on your OneToOneField?
https://docs.djangoproject.com/en/dev/topics/db/models/#specifying-the-parent-link-field
Jun, 2022 Update:
Yes, it's possible to have inline for one-to-one relation.
For example, as shown below, if "MyProduct" class has "models.OneToOneField()" referring to "Product" class which means "MyProduct" class has the ForeignKey referring to "Product" class:
# "models.py"
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
class MyProduct(models.Model):
name = models.CharField(max_length=100)
product = models.OneToOneField( # Here
Product,
on_delete=models.CASCADE,
primary_key=True
)
Then, you can inline "MyProduct" class under "Product" class as shown below:
# "admin.py"
from django.contrib import admin
from .models import Product, MyProduct
class MyProductInline(admin.TabularInline):
model = MyProduct
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
inlines = (MyProductInline, )
Oppositely, as shown below, if "Product" class has "models.OneToOneField()" referring to "MyProduct" class which means "Product" class has the ForeignKey referring to "MyProduct" class:
# "models.py"
from django.db import models
class MyProduct(models.Model):
name = models.CharField(max_length=100)
class Product(models.Model):
name = models.CharField(max_length=100)
my_product = models.OneToOneField( # Here
MyProduct,
on_delete=models.CASCADE,
primary_key=True
)
Then, you can inline "Product" class under "MyProduct" class as shown below:
# "admin.py"
from django.contrib import admin
from .models import Product, MyProduct
class ProductInline(admin.TabularInline):
model = Product
#admin.register(MyProduct)
class MyProductAdmin(admin.ModelAdmin):
inlines = (ProductInline, )

Categories