Django 1.2.1 Inline Admin for Many To Many Fields - django

I recently upgraded to Django 1.2.1 because I was specifically interested in the ability to have basic many-to-many inline fields. When using the admin like so:
Initial models:
class Ingredient(models.Model):
name = models.TextField()
class Recipe(models.Model):
ingredients = models.ManyToManyField(Ingredient)
Initial admin:
class IngredientInline(admin.TabularInline):
model = Recipe.ingredients.through
class RecipeOptions(admin.ModelAdmin):
inlines = [IngredientInline,]
exclude = ('ingredients',)
admin.site.register(Recipe,RecipeOptions)
What I got was the same form you would normally see on a ManyToMany field, with some extra rows. Supplying it with extra parameters like an Ingredient ModelForm did not help. Suspecting that something might be wrong with the basic ModelForm associations via model = Foo.manyfields.through, I decided to see if an intermediary model would help. It now displays a working inline form via:
New models:
class RecipeJoin(models.Model):
pass
class Recipe(models.Model):
ingredients = models.ManyToManyField(RecipeJoin,through='Ingredient')
class Ingredient(models.Model):
name = models.TextField()
test = models.ForeignKey(RecipeJoin,null=True,blank=True,editable=False)
New admin:
class IngredientInline(admin.TabularInline):
model = Recipe.ingredients.through
class RecipeOptions(admin.ModelAdmin):
inlines = [IngredientInline,]
admin.site.register(Recipe,RecipeOptions)
Obviously this is not a hack I'd like to use. Anyone know of a way to get a manytomany relationship to display via inline form without either (a) creating an entirely new BasicInline form and template or (b) putting it through an intermediary (or generic admin) model?
TIA. (I apologize for verbosity, it's my first post so wanted to be thorough).

Do one of these examples accomplish what you are trying to do?
a:
# Models:
class Ingredient(models.Model):
name = models.CharField(max_length=128)
class Recipe(models.Model):
name = models.CharField(max_length=128)
ingredients = models.ManyToManyField(Ingredient, through='RecipeIngredient')
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
amount = models.CharField(max_length=128)
# Admin:
class RecipeIngredientInline(admin.TabularInline):
model = Recipe.ingredients.through
class RecipeAdmin(admin.ModelAdmin):
inlines = [RecipeIngredientInline,]
class IngredientAdmin(admin.ModelAdmin):
pass
admin.site.register(Recipe,RecipeAdmin)
admin.site.register(Ingredient, IngredientAdmin)
b:
# Models:
class Recipe(models.Model):
name = models.CharField(max_length=128)
class Ingredient(models.Model):
name = models.CharField(max_length=128)
recipe = models.ForeignKey(Recipe)
# Admin:
class IngredientInline(admin.TabularInline):
model = Ingredient
class RecipeAdmin(admin.ModelAdmin):
inlines = [IngredientInline,]
admin.site.register(Recipe,RecipeAdmin)

If I remember correctly (and it's been a while since I've done this part), you need to add the admin for Ingredient and set it to have the custom ModelForm. Then that form will be used in the inline version of Ingredient.

Related

How to add a field to model with a decorator?

I'd like to add foreign keys to virtually every model in my app to allow different Sites. For this I think it would be nice to use decorators, because I will be able to only add those FKs under certain conditions (e.g. if Sites is installed).
Unfortunately my attempt doesn't do anything:
def add_site_fk_field(model_to_annotate: models.Model):
"""Add FK to Site to Model"""
model_to_annotate.site = models.ForeignKey(Site, on_delete=models.CASCADE)
return model_to_annotate
#add_site_fk_field
class Test(models.Model):
...
Is it somehow possible to do this? I prefet not to use abstract classes.
As I know you cant do it with decorator. But you can do that with creating an abstract base model class.
For example:
from django.db import models
class Site(models.Model):
name = models.CharField(max_length=60)
class AddSiteFkField(models.Model):
fk_site = models.ForeignKey(Site, on_delete=models.CASCADE)
class Meta:
abstract = True
class Test(AddSiteFkField):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)

DRY fields in Django models

I know about using inheritance and abstract models in reusing common fields in different models.
I'd like to know if the following approach is possible instead of inheritance and mixins.
from django.db import models
common_modified_by = models.CharField()
class Author(models.Model):
name = models.CharField()
modified_by = common_modified_by
class Book(models.Model):
title = models.CharField()
modified_by = common_modified_by
Will the above code work? Why or why not?
The issue with reusing the same field in multiple models is that the model attribute of the field will be set to the last model where the field is defined
from django.db import models
common_modified_by = models.CharField(max_length=20)
class Author(models.Model):
name = models.CharField(max_length=20)
modified_by = common_modified_by
class Book(models.Model):
title = models.CharField(max_length=20)
modified_by = common_modified_by
The field now has Book as it's model even when you get the field from the Author model. This can be problematic
>>> Book._meta.get_field('modified_by').model
<class 'foo.models.Book'>
>>> Author._meta.get_field('modified_by').model
<class 'foo.models.Book'>
One issue could be using the field as the target of a foreign key django.db.models.fields.related seems to use this model attribute quite a lot
It's also used when generating subqueries

Class based views how to handle OneToOneField in a model

I have 2 models:
class CompanyInfo(models.Model):
name = models.CharField(_('name'), max_length=100)
address = models.OneToOneField(Location)
class Location(models.Model):
address_1 = models.CharField(_("address"), max_length=128)
address_2 = models.CharField(_("address cont'd"), max_length=128, blank=True)
city = models.CharField(_("city"), max_length=64, default="")
state = USStateField(_("state"), default="")
zip_code = models.CharField(_("zip code"), max_length=5, default="")
When I use CBVs and prints out the form on the template. It shows name with an input field and address as a multiple choice selection.
Is there anyway in which I could convert the multiple choice to act as multiple input fields and the state to be multiple choice.
I figured I could have 2 form for these, but how would I incorporate a form inside of a form?
Also, the way in which the models and fields are must not be changed. For this example sure location fields could be in the company info, but I simply want how to do something similar when it doesn't make sense to include all these fields into the same model.
So, far only thing I can come up with is using function based views and just dealing with 2 forms at a time.
Here are my forms:
class CompanyInfoForm(ModelForm):
class Meta:
model = CompanyInfo
exclude = ['address']
class LocationForm(ModelForm):
class Meta:
model = Location
fields = '__all__'
then class based views:
class CompanyInfoCreate(CreateView):
model = CompanyInfo
form_class = CompanyInfoForm
class LocationCreate(CreateView):
model = Location
form_class = LocationForm
However, this is very helpful since these forms can only be done 1 at a time. I would like LocationView to be in place of the address location or of the sort.
Perhaps, these types of views have their strength in dealing with forms at an individual level.

Include Specific Foreign Keys in DRF Serializer

Here's two simple models to use as an example:
class Author(models.Model):
name = models.CharField(blank=True, max_length=50)
age = models.IntegerField(null=True, )
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author)
date = models.DateField()
Now what I'd like to do, is create a view for Book that pulls in one of the values from Author using the Django Rest Framework. Here's an example ModelSerializer:
class BookMetaSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('title','date','author__name',)
The trouble is that one can't access the fields of a foriegn key in the DRF like I gave above author__name. I haven't been able to figure out how to do this based on the documentation. All help is appreciated, thanks!
You can define author_name field with source argument to get the name of an author.
From the DRF docs on source argument:
The name of the attribute that will be used to populate the field. May
be a method that only takes a self argument, such as
URLField('get_absolute_url'), or may use dotted notation to traverse
attributes, such as EmailField(source='user.email').
Final Code:
class BookMetaSerializer(serializers.ModelSerializer):
# use dotted notation to traverse to 'name' attribute
author_name = serializers.CharField(source='author.name', read_only=True)
class Meta:
model = Book
fields = ('title','date','author_name',)

Django OneToOneField and Admin UI

I am having a confusion on how to model a relationship in Django so that it can be edited inline in the Django Admin.
Let me explain the scenario.
I have a Customer Model and An Address Model. In the customer model I have a OneToOneField relationship to Address once for billing and once for shipping address.
class Address(models.Model):
pass
class Employee(models.Model):
billing_address = models.OneToOneField(Address)
shipping_address = models.OneToOneField(Address)
# Many more such fields
Now with this model there is no easy way to make them inline in Admin. I have tried the following
class AddressInline(admin.TabularInline):
model = Address
class Customer(admin.ModelAdmin):
inlines = [AddressInline, ]
I keep getting an error,
<class 'employee.admin.AddressInline'>: (admin.E202) 'employee.Address' has no ForeignKey to 'employee.Customer'.
Now I know there are other bugs similar to this one. ie. Use OneToOneField inlined in Django Admin and Django admin - OneToOneField inline throws "has no ForeignKey" exception
But I think my question is slightly different to warrant this post. Please help!
#1
models.py
class BillingAddress(models.Model):
employee = models.OneToOneField(to=Employee, related_name='billing_address')
class Employee(models.Model):
# Many more such fields
admin.py
class AddressInline(admin.TabularInline):
model = BillingAddress
extra = 1
#admin.register(Employee)
class EmployeeAdmin(admin.ModelAdmin):
inlines = [AddressInline, ]
#2
models.py
class Address(models.Model):
name = models.CharField(max_length=250)
employee = models.ForeignKey(to=Employee, related_name='addresses')
class Employee(models.Model):
# Many more such fields
admin.py
class AddressInline(admin.TabularInline):
model = Address
#admin.register(Employee)
class EmployeeAdmin(admin.ModelAdmin):
inlines = [AddressInline, ]
Full Example:
models.py
class Company(models.Model):
pass
class CompanyScheduler(models.Model):
company = models.OneToOneField(
to=Company,
related_name='scheduling',
on_delete=models.CASCADE,
)
start = models.DateField()
finish = models.DateField()
admin.py
class CompanySchedulerInLine(admin.TabularInline):
model = CompanySchedulerInLine
extra = 1
#admin.register(CompanyModelAdmin)
class CompanyModelAdmin(admin.ModelAdmin):
inlines = [
SchedulerInLine,
]
UI