I can add Criterias to a place. How can I add Places to a criteria?
Models:
class Criterias(models.Model):
name = ...
class Places(models.Model):
name = ...
class PlacesToCriterias(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
criteria_group = models.ForeignKey(Criterias)
Admin - PLACES part:
class PlaceCriteriasInlineAdmin(GenericTabularInline):
model = PlacesToCriterias
class PlacesAdmin(admin.ModelAdmin):
inlines = [PlaceCriteriasInlineAdmin]
admin.site.register(Places, PlacesAdmin)
In this case, when I open Places admin change page, I can add Criterias items to my 'place'.
Admin - CRITERIAS part:
class CriteriaPlacesInlineAdmin(GenericTabularInline):
model = PlacesToCriterias
class CriteriasAdmin(admin.ModelAdmin):
inlines = [CriteriaPlacesInlineAdmin]
admin.site.register(Criterias, CriteriasAdmin)
In this case, when I open Criterias admin change page, I CAN NOT add Places item to my 'criteria', because instead of possible places I see criterias.
How to get Places items at Criterias admin page?
It was quite easy. GenericTabularInline must be changed to admin.TabularInline
class CriteriaPlacesInlineAdmin(admin.TabularInline):
model = PlacesToCriterias
class CriteriasAdmin(admin.ModelAdmin):
inlines = [CriteriaPlacesInlineAdmin]
admin.site.register(Criterias, CriteriasAdmin)
If anyone needs to get dropdown list with selected object, instead of content_type and object_id fields, the solution is here.
Related
My models are using GenericForeignKey and GenericRelations. For simplification my models structure is following:
class BaseModel(models.Model):
name = models.CharField(max_length=100)
model_labels = models.GenericRelation('ModelLabel', related_query_name='%(class)s')
model_types = models.GenericRelation('ModelType'), related_query_name='%(class)s')
class Meta:
abstract = True
class ModelA(BaseModel):
fieldA = models.CharField(max_length = 50)
class ModelB(BaseModel):
fieldB = models.CharField(max_length = 50)
class ModelLabel(models.Model);
label = models.CharField(max_length = 50)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
model_object = GenericForeignKey('content_type', 'object_id')
class ModelType(models.Model):
type = models.CharField(max_length = 50)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
model_object = GenericForeignKey('content_type', 'object_id')
class Unrelated(models.Model):
name = models.CharField(max_lenght = 50)
The problem I am facing is in admin view for ModelLabel and ModelType. Right now, out-of-the-box I am getting 3 fields: Label or Type, Content type and Object id. Yes, this is correct according to the model.
But in Content type I am getting not only ModelA and ModelB as an options. I'm also getting Unrelated and most of the individual fields of different models. According to documentation:
Instances of ContentType represent and store information about the
models installed in your project, and new instances of ContentType are
automatically created whenever new models are installed.
According to this Unrelated can appear in the values, but models' fields shouldn't. So why they appear?
However, this is not the main problem I am struggling with. I'd like to narrow the list of options to choose from in Content type just to ModelA and ModelB. From what I gathered since this is ForeignKey under the hood I should be able to go with ModelChoiceField, but I would need to make it use some hardcoded QuerySet but I cannot find how to make it e.g. from dictionary. Similar goes with Object id, where I'd like to be able to select what value I want to use based on available numbers.
So far I realized that most likely I need to extend admin.ModelAdmin view and most likely also create custom form (extend forms.ModelForm). This is also theory because so far I couldn't figure a way to get mentioned custom QuerySet or Object id. Also there is some drawback of this approach, because if I'll choose to go with this, I'll need to have custom form for every model (ModelLabel, ModelType) - is this really necessary?
I know of a way to add GenericInlineModelAdmin class, but this view allows to add object of type ModelLabel together with ModelB. This doesn't cover case where object of type ModelB already exists.
To summarize what are main goals I want to achieve:
Fix Admin view to create ModelLabel and ModelType
Narrow list of options for Content type field to ModelA and ModelB
Allow user to choose only existing IDs for Object id field
Imagine we have a model like that:
class Container(models.Model):
name = models.CharField(max_length=60)
class Element(models.Model):
container = models.ForeignKey(Container, blank=True, null=True)
Container is the One, Element is the many.
In Django admin, if I add a StackedInline with model=Element to the inlines of the Container model admin:
class Inline(admin.StackedInline):
model = Element
class ContainerAdmin(admin.ModelAdmin):
inlines = (Inline,)
admin.site.register(Container, ContainerAdmin)
I end up with a formset allowing me to enter new Element objects on the Add Container form.
Instead, I would like to be given a select widget, to pick existing Element objects.
Is that possible without introducing an extra model ?
I think you should be able to do it like this:
class ContainerAdminForm(forms.ModelForm):
class Meta:
model = Container
fields = ('name',)
element_set = forms.ModelMultipleChoiceField(queryset=Element.objects.all())
class ContainerAdmin(admin.ModelAdmin):
form = ContainerAdminForm
# register and whatnot
I don't know that I have anything like this in my project, but I'll let you know if I find something. You may also have to override the save() method on the form in order to actually save the selected Elements; I don't know if naming the field element_set (or whatever the name of the reverse relation is) will be enough.
I have models like below
class Product(models.Model):
...
class ProductQuantity(models.Model):
product = models.ForeignKey('Product')
invoice = models.ForeignKey('Invoice')
quantity = models.IntegerField()
class Invoice(models.Model):
...
products = models.ManyToManyField(Product, through=ProductQuantity)
In django admin I would like to change quantity not by opening new dialog, which is basically how admin behaves, instead selecting one of them if any, or type there and change values directly from that window.
You can use an inline:
class ProductQuantityInline(admin.StackedInline):
model = ProductQuantity
class InvoiceAdmin(admin.ModelAdmin):
inlines = [ProductQuantityInline]
That way, you can edit the ProductQuantity directly on the Invoice admin page, without any extra dialogs.
my models.py
class PagePlaceholder(models.Model):
content_type = models.ForeignKey()
object_id = models.PositiveIntegerField()
display_content = generic.GenericForeignKey('content_type', 'object_id')
class Page(models.Model):
page_name = models.CharField(max_length=1000,default="sample_page")
placeholder = models.ManyToManyField('PagePlaceholder')
the placeholder can refer to any of 4 classes by the generic foreign key relationship.
page has a many2many relationship to placeholder to say that a page can contains a bunch of different models.
and my admin.py for the same.
class PagePlaceholderAdmin(admin.ModelAdmin):
pass
class PageInline(GenericStackedInline):
model = PagePlaceholder
class PageAdmin(admin.ModelAdmin):
inlines = [PageInline,]
now what i want is in the page admin view, to show the form for editing the pageplaceholder's display content object. trying to get that inline gives me an error saying that object is not a class.
is there a decent solution for this with the admin.py or is it necessary for me to create a new form and view to display these model forms properly?
Imagine this model:
class ExercisePart(models.Model):
exercise = models.ForeignKey(Exercise)
sequence = models.IntegerField()
class Meta:
unique_together = (('exercise', 'sequence',),)
class Exercise(models.Model):
name = models.CharField(max_length=15)
From the admin interface I'd like to be able to create/link ExerciseParts through the Exercise page. I'd like to do that because I wish to avoid having to go on another page each time I want to add an ExerciseParts.
Is it possible ? How can I do that ?
You're looking for the inline admin feature.
admin.py
class ExercisePartInline(admin.TabularInline):
model = ExercisePart
class ExerciseAdmin(admin.ModelAdmin):
inlines = [ExercisePartInline]