I attempt to add a GenericTabularInline to the admin of a related model and the ManyToMany field of the Generic Relation model does not display anything.
models.py
class RelatedIngredients(models.Model):
content_type = models.ForeignKey(ContentType, verbose_name='Content Type')
object_id = models.PositiveIntegerField(verbose_name='Object ID')
content_object = fields.GenericForeignKey('content_type', 'object_id')
ingredients = models.ManyToManyField(Ingredients)
admin.py
class IngredientInline(GenericTabularInline):
model = RelatedIngredients
extra = 0
min_num = 1
max_num = 1
can_delete = False
fields = ['ingredients',]
filter_horizontal = ['ingredients',]
#admin.register(Cake)
class CakeAdmin(admin.ModelAdmin):
fields = ['name', 'description']
inlines = [IngredientInline,]
The proper filter horizontal widget appears but it appears empty. Nothing in the available or chosen sides. Now I know the model works as I tried to do RelatedIngredients.ingredients as a Foreign Key and the GenericTabularInline worked.
What's the difference? Any ideas?
Related
I'm trying to accomplish a three-level stacked inline form in Django. Suppose these models:
class Anuncio(models.Model):
title = models.CharField(max_length=200)
delivery = models.CharField(max_length=100)
class Product(models.Model):
anuncio = models.ForeignKey(Anuncio, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
quantity = models.PositiveIntegerField(default=1)
price = models.PositiveIntegerField()
class Image(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
image = models.ImageField()
There is a relation Anuncio-Product and another relation Product-Image. With this Django package, I accomplished exactly what I want in the Django admin: when creating an Anuncio object, I can add as many Products as I want, and those products can have as many Images as I want. I'm trying to accomplish this in the front end.
I think the way to go is with Django formsets, but I'm facing some problems. All the resources I've been able to find online are only 'two-level' formsets or in 'three-level' cases all the foreign keys point to the same parent model.
With this forms.py file:
class ProductForm(ModelForm):
class Meta:
model = Product
fields = ['name', 'quantity', 'price']
class ImageForm(ModelForm):
class Meta:
model = Imagen
fields = ['image']
class AnuncioForm(ModelForm):
class Meta:
model = Anuncio
fields = ['title', 'delivery']
And this views.py function:
def anunciocreateview(request):
form = AnuncioForm(request.POST or None)
ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)
if all([form.is_valid(), ProductFormSet.is_valid(), ImageFormSet.is_valid()]):
parent = form.save(commit=False)
parent.anunciante = request.user
parent.save()
for form in ProductoFormSet:
child = form.save(commit=False)
child.anuncio = parent
child.save()
for form in ImagenFormSet:
imagen = form.save(commit=False)
imagen.product = form.product
imagen.save()
context = {
'form_1' : form,
'form_2' : ProductFormSet,
'form_3' : ImageFormSet,
}
But I think I'm missing important points when it comes to add the proper relations between models. This set-up gives an AttributeError of: 'ProductForm' object has no attribute '__name__'
The, for example, 'add (extra) Product' that appears in AdminStackedInLine I guess it can be accomplished with JavaScript, playing with hidden forms and changing attributes on click events.
Anyone has experience doing something similar or can guide me through the correct direction? Also on how to manage the data and the relations of the submitted forms?
I think your problem is you have tried to validate a class Form instead of instanciate your formset and validate them.
Your code would be look like to something like that :
def anunciocreateview(request):
ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)
anuncio_form = AnuncioForm(request.POST or None)
product_formset = ProductFormSet(request.POST or None)
image_formset = ImageFormSet(request.POST or None)
if all([form.is_valid(), product_formset.is_valid(), image_formset.is_valid()]):
...
The function inlineformset_factory just create a Form class, not a instance of form.
More information and example on the documentation : https://docs.djangoproject.com/fr/4.1/topics/forms/formsets/
These are my models here:
class Site(models.Model):
siteID = models.CharField(max_length=255, primary_key=True)
class EndDevice(models.Model):
class Meta:
unique_together = ("edevID", "siteID")
edevID = models.CharField(max_length=255)
siteID = models.ForeignKey(Site, related_name='endDeviceList', on_delete=models.CASCADE)
deviceCategory = models.BigIntegerField()
This is my serilaizer:
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = EndDevice
fields = ("edevID", "siteID", "deviceCategory")
class SiteSerializer(serializers.ModelSerializer):
endDeviceList = DeviceSerializer(many = True, read_only=True)
class Meta:
model = Site
fields = ("siteID", "endDeviceList")
This is my view:
class IndividualSite(generics.RetrieveUpdateDestroyAPIView):
'''
PUT site/{siteID}/
GET site/{siteID}/
DELETE site/{siteID}/
'''
queryset = EndDevice.objects.all()
serializer_class = SiteSerializer
I am trying to get/put/delete results using this class and I am trying to get all the EndDevice instances which have same siteID. But my serialzer only shows the siteID and doesn't show the endDeviceList (which should have the instants of the model EndDevice)
The problem is quite similar to this link:django rest-farmework nested relationships.
I have been trying different ways to serialize the objects, I think this is probably the smartest way, but have been really unsucccessful. Any help will be appreciated.
The urls.py:
urlpatterns = [
urlpatterns = [path('site/<str:pk>/', IndividualSite.as_view(), name = "get-site"),]
And it is connected to the main urls.
you are using read_only field for the Foreign relationship, remove that, as read_only wont display them
class SiteSerializer(serializers.ModelSerializer):
endDeviceList = DeviceSerializer(many = True)
There is a 'league_type' field in my form and I want to make it readonly. I used widget.attr['readonly'] = True but it doesn't work.
The model:
class League(models.Model):
league_types = (
('league', 'League'),
('knockout', 'Knockout'),
)
....
season = models.ForeignKey(Season, related_name = "league_season")
league_type = models.CharField(max_length=10, choices = league_types, default ='league')
...
The ModelForm:
class LeagueForm(forms.ModelForm):
class Meta:
model = League
fields = ('title', 'league_type', 'season', 'status')
widgets = {'season':forms.HiddenInput(),}
view.py:
if League.objects.filter(season = season, league_type = 'knockout').count():
form = LeagueForm(initial={'season': season, 'league_type': 'league'})
form.fields['league_type'].widget.attrs['readonly'] = True
else:
form = LeagueForm(initial={'season': season})
Update:
I cannot use disabled attribute because I'm going to create a form with league_type initial value.
I have the following models.py file:
class Product(Model):
...
class ExtraService(Model):
...
class Order(Model):
...
class OrderItem(Model):
order = models.ForeignKey(verbose_name=_('Order Item'), to=Order)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
item_relation = GenericForeignKey('content_type', 'object_id')
quantity = models.PositiveIntegerField(verbose_name=_('Quantity'), default=0)
And I'm working my admin.py this way:
class OrderItemInlineAdmin(GenericTabularInline):
model = OrderItem
min_num = 0
extra = 0
fields = ('item_relation', 'quantity',)
ct_field = 'content_type'
ct_fk_field = 'object_id'
class OrderAdmin(admin.ModelAdmin):
list_display = ('user_form',)
inlines = (OrderItemInlineAdmin,)
admin.site.register(Order, OrderAdmin)
I can't get my admin to show the OrderItem object inline (which can have a key to either a Product or an ExtraService) with my Order object, followed by its quantity field. Instead, it says the item_relation field is unknown:
FieldError at /admin/product/order/13/
Unknown field(s) (item_relation) specified for OrderItem
How do I bypass this?
PS: I've also tried using my own ModelForm but it still doesn't recognize the item_relation field.
PS1: If I don't define a fields variable in OrderItemInlineAdmin, I end up with something like this, which is incorrect because I have existing OrderItem objects and this assumes I don't (no object selected and no quantity?):
models.py:
class Player(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(max_length=50)
class Tournament(models.Model):
name = models.CharField(max_length=50)
class TournamentPlayer(models.Model):
tournament = models.ForeignKey(Tournament)
player = models.ForeignKey(Player)
paid = models.BooleanField()
def player_email(self):
return self.player.email
admin.py:
class TournamentPlayerInline(admin.TabularInline):
model = TournamentPlayer
fields = ('player', 'paid', 'player_email')
#admin.register(Tournament)
class TournamentAdmin(admin.ModelAdmin):
inlines = [TournamentPlayerInline]
I have an Inline question. When I pull up a Tournament in the Admin Site, I can see which players are going, and if they paid. I would also like to display extra information contained in Player, for example email address.
In TournamentPlayerInline I thought I might be able to get away with fields = ('player', 'paid', 'player_email') but I get FieldError: Unknown field(s) (player_email) specified for TournamentPlayer.
I also tried fields = ('player', 'paid', 'player__email'), but I get FieldError: Unknown field(s) (player__email) specified for TournamentPlayer.
If I move player_email from fields to readonly_fields, I no longer get the error, but the player email also isn't displayed either.
This is what I'm after:
How can I access Player properties from TournamentPlayerInline?
Django 1.8.4
Monkey's answer is almost correct. The only change you have to make is to your admin.py, and it's merely adding 'player_email' to both fields as well as readonly_fields. Changing the position of 'player_email' in fields will allow you to order it as per your example.
class TournamentPlayerInline(admin.TabularInline):
model = TournamentPlayer
fields = ('player', 'player_email', 'paid',)
readonly_fields = ('player_email',)
#admin.register(Tournament)
class TournamentAdmin(admin.ModelAdmin):
inlines = [TournamentPlayerInline]
If you do not require the player_email to be editable from the inline, then you can accomplish this with the readonly_fields variable:
class TournamentPlayerInline(admin.TabularInline):
model = TournamentPlayer
fields = ('player', 'paid')
readonly_fields = ('player_email',)
#admin.register(Tournament)
class TournamentAdmin(admin.ModelAdmin):
inlines = [TournamentPlayerInline]
As an alternative, you do not have to define your custom property in your model if you're not using it directly, and just want to view it in admin -- you can create it in the Inline via a mixin:
models.py
class Player(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(max_length=50)
class Tournament(models.Model):
name = models.CharField(max_length=50)
class TournamentPlayer(models.Model):
tournament = models.ForeignKey(Tournament)
player = models.ForeignKey(Player)
paid = models.BooleanField()
admin.py
class PlayerEmailMixin(object):
def player_email(self, obj):
return obj.player.email
player_email.short_description = "Player Email"
class TournamentPlayerInline(PlayerEmailMixin, admin.TabularInline):
model = TournamentPlayer
fields = ('player', 'player_email', 'paid', )
readonly_fields = ('player_email',)
#admin.register(Tournament)
class TournamentAdmin(admin.ModelAdmin):
inlines = [TournamentPlayerInline]
You could also make it a mailto URI this way:
class PlayerEmailMixin(object):
def player_email(self, obj):
return '<strong>{0}</strong>'.format(obj.player.email)
player_email.short_description = "Player Email"
player_email.allow_tags = True
This is known to work in Django 1.9.5