django-mptt nested dropdown in standard ModelAdmin? - python

I'm using django-mptt for a Category model, which is a foreign key to a Project model:
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
name = models.CharField(max_length=255)
parent = TreeForeignKeyY('self',
null=True,
blank=True,
related_name='children'
)
class Project(models.Model):
name = models.CharField(max_length=255)
category = models.ForeignKey('Category')
It's easy to set up the Category admin using MPTTModelAdmin, complete with a nice nested dropdown for picking parent category:
from django.contrib import admin
from mptt.admin import MPTTModelAdmin
from myapp.models import Category, Project
admin.site.register(Category, MPTTModelAdmin)
Now I'd like to include a nice nested Category dropdown in my Project admin, but the standard admin.ModelAdmin does not include this functionality and subclassing MPTTModelAdmin doesn't seem to work:
Project has no field named 'parent'
Is it possible to mimic the nested dropdown in a non-MPTT admin tool?

You can still use TreeForeignKey on a non-MPTTModel, assuming that the linked model is an MPTTModel, i.e.:
class Project(models.Model):
name = models.CharField(max_length=255)
category = TreeForeignKey('Category')
Alternatively, you can make the field an instance of mptt.forms.TreeNodeChoiceField or mptt.forms.TreeNodeMultipleChoiceField on your form.

Related

The model is not displayed in the django admin panel

I don't have the advertisement module displayed in the django admin panel. Here is the model code
from django.db import models
class Advertisement(models.Model):
title = models.CharField(max_length=1000, db_index=True)
description = models.CharField(max_length=1000, default='', verbose_name='description')
creates_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
price = models.FloatField(default=0, verbose_name="price")
views_count = models.IntegerField(default=1, verbose_name="views count")
status = models.ForeignKey('AdvertisementStatus', default=None, null=True, on_delete=models.CASCADE,
related_name='advertisements')
def __str__(self):
return self.title
class Meta:
db_table = 'advertisements'
ordering = ['title']
class AdvertisementStatus(models.Model):
name = models.CharField(max_length=100)
admin.py /
from django.contrib import admin
from .models import Advertisement
admin.site.register(Advertisement)
I was just taking a free course from YouTube. This was not the case in my other projects. Here I registered the application got the name in INSTALLED_APPS. Then I performed the creation of migrations and the migrations themselves. Then I tried to use the solution to the problem here , nothing helped. I didn't find a solution in Google search either.
127.0.0.1:8000/admin/
console
admins.py
The name of the file is admin.py not admins.py. Yes, that is a bit confusing since most module names in Django are plural. The rationale is probably that you define a (single) admin for the models defined.
Alternatively, you can probably force Django to import this with the AppConfig:
# app_name/apps.py
from django.apps import AppConfig
class AppConfig(AppConfig):
def ready(self):
# if admin definitions are not defined in admin.py
import app_name.admins # noqa

Django admin - Inline with choices from database

I need some basic help with the django admin site. What I basically want to do is to be able to populate an inline with choices from the database. For example consider the following models:
class Item(models.Model):
description = models.CharField(max_length=100)
class Category(models.Model):
name = models.CharField(max_length=100)
item = models.ForeignKey(Item, on_delete=models.CASCADE, null=True, blank=True)
And in admin.py I have the following setup:
class CategoryAdminForm(forms.ModelForm):
name = forms.ChoiceField(choices = category_service.get_all_categories())
class CategoryInline(admin.TabularInline):
model = Category
form = CategoryAdminForm
class ItemAdmin(admin.ModelAdmin):
inlines = [CategoryInline]
admin.site.register(Item, ItemAdmin)
admin.site.register(Category)
What I want to be able to do is to insert categories into db, and when I want to insert an item, the categories inline to be populated with categories from the db.
With the current setup it is not working. It says that category is not an iterable object. What am I missing here?
You should replace your ChoiceField with a ModelChoiceField. They allow you to specify a queryset to populate the choices.
category = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label="(Nothing)")

How to use filter_horizontal on a ForeignKey between two apps using ContentTypes in Django admin?

Say I have this app named Pantry that is to connect to any other app I may come along. To keep the app decoupled, generic relations are used through the model LinkedItem which connects the Ingredients model to apps outside Pantry.
I would like the content on the other end of the generic relation, say an app named Bakery, to be able to do a filter_horizontal with Ingredients.
Pantry
models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields
class Ingredient(models.Model):
'''
Model containing all the ingredients, their slugs, and their descriptions
'''
name = models.CharField(unique=True, max_length=100)
slug = models.SlugField(unique=True, max_length=100)
description = models.CharField(max_length=300)
# method to return the name of the db entry
def __str__(self):
return self.name
class LinkedItem(models.Model):
'''
Model that links ingredients to various other content models
'''
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = fields.GenericForeignKey('content_type', 'object_id')
ingredient = models.ForeignKey(Ingredient)
# method to return the name of the db entry
def __str__(self):
return self.ingredient.name
# defines options for the model itself
class Meta:
unique_together = (('content_type','object_id')) # prevents duplicates
Bakery
admin.py
from django.contrib import admin
from bakery.models import Cake
class CakeAdmin(admin.ModelAdmin):
filter_horizontal = ('') # what to put here so ingredients show up?
Any ideas?
I think this is what the GenericRelation is for, so you need to add one to your Cake model and use it's name in your CakeAdmin.
But you need to use Inlines if you don't want to do a lot of workaround as M2M fields are not supported for relations with intermediary models.

Django Admin - How can I select new data created using ForeignKey?

For example, I have this models
# models.py
from django.db import models
class Category(models.Model):
title = models.CharField(max_length=60)
class CategoryDescription(models.Model)
category = models.ForeignKey(Category)
description = models.CharField(max_length=120)
class Image(models.Model):
category = models.ForeignKey(Category)
description = models.ForeignKey(CategoryDescription)
image = models.ImageField()
And I have this admin init file
# admin.py
from django.contrib import admin
from .models import Category, CategoryDescription, Image
class CategoryDescriptionInline(admin.TabularInline):
model = CategoryDescription
class CategoryAdmin(admin.ModelAdmin):
inlines = (CategoryDescriptionInline,)
class ImageAdmin(admin.ModelAdmin):
pass
admin.site.register(Category, CategoryAdmin)
admin.site.register(Image, ImageAdmin)
I want create new Image in Admin, i create new category, using plus placed right from select input and next, i want choose Image Description, but i cant do it, because it isn't loaded.
What you may recommend for me in this situation? Thanks
Register a ModelAdmin for CategoryDescription, e.g.:
admin.site.register(CategoryDescription)

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