Django form: ManyToMany inline creation - python

I would like to be able to let users create locations "on-the-fly" when they create a report. Using the following models if possible.
models:
class Report(models.Model):
...
location = forms.ManyToManyField(Location)
class Location(models.Model):
name = forms.CharField(max_length=255)
...
The behavior I am searching for is close to the one given by the django formsets.
Indeed I am currently able to somewhat do that using instead a foreignkey relationship and relying on formsets. Using this technic,django-extra-views and django-dynamic-formset, I am even able to let users choose how many locations they want to add.
A strip down version of the actual setup :
models:
class Report(models.Model):
...
class Location(models.Model):
name = forms.CharField(max_length=255)
report = forms.ForeignKey('Report')
forms:
class LocationForm(forms.ModelForm):
class Meta:
model = Location
fields = '__all__'
class LocationFormSet(extra_views.InlineFormSet):
model = TestLocation
form_class = TestLocationForm
views:
class ReportCreateView(extra_views.CreateWithInlinesView):
model = Report
inlines = [LocationFormSet, ]
Unfortunately using this setup, a location can not belong to more than one report which bring a lot of duplicated location. That's why I think the ManyToMany relationship would be better suited. I stumbled upon some answers related to this question that talked about intermediary model but I wasn't able to go anywhere on my own to make it work.
Is their a way to tweak my working setup to make it work with a manytomany relationship? Or does it exists a completely different approach to let user create "on-the-fly" the locations during the report creation?
Thank's!

For create "on-the-fly" I had changed ManyToMany by ForeingKey.
class Report(models.Model):
...
location = forms.ManyToManyField(Location) # REMOVE
class Location(models.Model):
report = models.ForeignKey(Report) # ADD
name = forms.CharField(max_length=255)
...
And then, InlineForms will work.

Related

Inheritance model update to its parent model

I need extend a model from another model.
Case:
core/models.py
class Master(models.Model):
code = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=100, blank=False, null=False)
class Meta:
abstract = True
class City(Master):
zipcode = models.IntegerField()
custom/models.py
from core.models import City
class City(City)
newfield = models.CharField(max_length=20)
custom is an app.
I have tried with proxy model but it is not what I need, since proxy model adds a new table. https://docs.djangoproject.com/en/2.2/topics/db/models/#proxy-models
I need is that when I migrate add the new field to City.
More info.
In core the table is created and in custom you can add new fields that the client needs. The idea is that core is only maintained as standard.
Proxy models don't add new tables. From the docs link you mentioned:
The MyPerson class operates on the same database table as its parent Person class.
If you want one table called core_city, and another called custom_city, the second one having an extra field, you simply subclass it. Perhaps it would be easier to use an alias:
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
custom_city will have all fields from core_city, plus a newfield. The description of how this works (and an example) is covered in the docs section Multi-table inheritance.
If what you want is to have one single database table, then you should use a proxy Model, however they really don't allow you to create new fields. The field should be created in the parent model, or otherwise exist in the database and not be handled by Django migrations at all.
You are looking for Abstract base classes models:
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class.
This is the base class:
#core/models.py
class City(Master):
zipcode = models.IntegerField()
class Meta:
abstract = True # <--- here the trick
Here your model:
#custom/models.py
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.
You can update or create your class constants after its defined like this
from core.models import City
City.newfield = models.CharField(max_length=20)
You may need to use swappable models, using them you can define a City class and change it with whichever model you need later,
but that way you can't import and use the base City model directly, you may need to provide a method like get_city_model for that, as your public API.
class City(Master):
zipcode = models.IntegerField()
class Meta:
swappable = 'CORE_CITY_MODEL'
and maybe replace it later with some other model, then just set CORE_CITY_MODEL to that model in the form of 'app_name.model_name'.
The django.contrib.auth is a good example of this, you may consider checking User model and get_user_model method. Although I think you may face problems if you change your city model after you did run migrate, it may not move your data to the new table, but I'm not sure about this.

How to Design my data model in django with many OneToMany relations

I have an entity in my application which has some attribute like auth.User but it has also some extra attributes. So I created a OneToOne relationship with auth.User
class UserEntity(models.Model):
user = models.OneToOneField(User)
... other fields ...
I also have a Person model which is UserEntity so created it as this:
class Person (models.Model):
userEntity = models.OneToOneField(UserEntity)
... other fields ...
There are many different attributes like addresses,experiences,education and other details that I want to associate with my Person. A Person can have many addresses,experiences,education,speciality
I have a Speciality Model like this.
class Speciality(models.Model):
name = models.CharField()
code = models.CharField()
... other fields ...
The problem is how should I my data model be designed so that I can retrieve a person/user with all addresses,expereiences,specialities etc. I can associate each Person/user with Speciality since Speciality is an independent model and can exist without a user. Currently I have created another model for each e.g for Speciality I have created
class PersonSpeciality(models.Model):
person = models.ForeignKey(Person)
specialities = models.ForeignKey(Speciality)
Can I design it in a better way so I can have fast searching and retrievals and no mess.
Thank you
If a Person can have multiple Specialities, then you can implement a oneToMany relationship, just by adding a foreignKey:
class Speciality(models.Model):
name = models.CharField()
code = models.CharField()
person = models.ForeignKey(Person)
However, if you have several Persons with the same specialities (let's say 10 Persons are Painters), you would have 10 different Painters entries.
To avoid duplication, you can implement a ManyToMany relationship :
class Speciality(models.Model):
name = models.CharField()
code = models.CharField()
person = models.ManyToManyField(Person)
Django will create a third table in your DB to handle that (you don't need to write your PersonSpeciality class).

Model redundant django tables?

I am working to figure out the model for a Django project: an app to track Books.
Among other fields, every Book has either/both a Printer and a Publisher, which are basically identical. So, here's how it stands:
class Book(models.Model):
title = models.CharField(max_length=100)
printer = models.ForeignKey('Printer')
publisher = models.ForeignKey('Publisher')
class Printer(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
class Publisher(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
It seems to me this is bad database form: it's not DRY. In addition, quite often, a Book might be printed by a firm which publishes the same or another book: in other words, the tables can overlap. So, the two models Printer and Publisher should really be combined, while they need to remain distinct in the admin.
My question: how best to do this? Should I create another model, Firm, and create one-to-one relationships between it and Printer/Publisher?
The Django way to handle that is to create an Abstract Base Model. This is the DRY way to create your models. Here is the code:
class BaseModel(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
class Meta:
abstract = True
class Printer(BaseModel):
pass
class Publisher(BaseModel):
pass
This will allow you to specify redundant fields only once. Also, if you need to add any extra fields to one model, just add them instead of using pass.

Django Form with a one-to-many relationship

I have a form in Django called PersonForm this forms model has a one-to-many relationship with Car. When displaying the PersonForm just like in the Django Admin I would like to allow my users to select/deselect from a list of Cars etc. Is this possible? I'm looking for information on where to start.
This is what I have so far for the PersonForm:
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ('description',)
The Models:
class Person(models.Model):
description = models.CharField(max_length="150")
class Car(models.Model):
make = models.CharField(max_length="25")
owner = models.ForeignKey('Person', related_name="Car")
So in the person form I need to show a list of cars that person is the owner of an allow selecting/deselecting of them. I'm assuming I can do this in the form i.e. using something like the related name.
Sounds like you want an inline model form. This give you the ability to add/remove Car objects from a Person within the Person form.
That previous link was for inlinemodeladmin. This next link is for an inline form:
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#modelforms-factory
I didn't have any chance with inline formset, so i would suggest to override your save method of the model, i feel it's more DRY:
class PersonForm(forms.ModelForm):
# add a field to select a car
car = forms.ModelChoiceField(car.objects.all())
class Meta:
model = Person
fields = ('description', 'car')
def save(self, commit=True):
instance = super().save(commit)
# set Car reverse foreign key from the Person model
instance.car_set.add(self.cleaned_data['car']))
return instance
I know this is an old thread, but since I found myself almost exclusively pointed here by google when searching, I thought I would include the following for anyone else looking for an answer.
The answer, I think, is to use
https://docs.djangoproject.com/en/3.1/ref/forms/fields/#modelchoicefield
or
https://docs.djangoproject.com/en/3.1/ref/forms/fields/#modelmultiplechoicefield
There is a good article on how to use the modelmultiplechoicefield at :
https://medium.com/swlh/django-forms-for-many-to-many-fields-d977dec4b024
But it works for one to many fields as well. These allow us to generate a form with multiple choices as checkboxes or similar widgets based upon a related field in a model.

User-defined text fields in django admin

Django (and database) newbie here.
I'm trying to figure out a way to enable the creation of n custom text fields for a table (let's call it Book) using the admin interface. I would like a way for the user to define new text fields through the admin interface (instead of defining fields like CustomField1, CustomField2, etc, through a model followed by running manage.py syncdb).
Ultimately, I would want to create a separate table called CustomFields. The django admin user (who is not a programmer), would go and enter the custom text fields in this table (e.g. pubdate, isbn, country). Then, when doing data entry for a Book, they would hit "+" for every custom field they wanted, have them available in a dropdown, and add accompanying text for the custom field. The text entered for each field is specific to the parent Book.
Any suggestions? I have a feeling there's a simple solution that I'm somehow not grasping here.
Where you might run into problems is because Django will not recreate tables or columns based on changing model declarations. This means you're unable to add fields to a table at run-time without running the generated sql on your database manually. What you'll need, instead, is a way of defining custom fields, and linking them to your model.
What I'd suggest is the following:
class CustomField(models.Model):
name = models.CharField(max_length=32)
class Book(models.Model):
... fields
class CustomValue(models.Model):
field = models.ForeignKey(CustomField)
value = models.CharField(max_length=255)
book = models.ForeignKey(Book)
The validation on the above is fairly non-existant, and this doesn't allow you to define required custom fields for each model, but you should be able to come up with that part if you need it later on.
# taken and modified from django online tutorial
class CustomValueInline(admin.StackedInline):
model = CustomValue
extra = 3
class BookAdmin(admin.ModelAdmin):
fieldsets = [
# your fields in here
]
inlines = [CustomValueInline]
admin.site.register(Book, BookAdmin)
This will allow users to select up to 3 custom fields and values directly, with the option to add more if they wish.
Edit: Changed the answer to reflect further information in comments.
For the beginning you can create one model for the book and one for the text field, both connected through a foreign key relation. You can easily administrate this then through django's inline admins, which will enable you to add more text fields!
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
class TextField(models.Model):
text = models.TextField
book = models.ForeignKey(Book)
# admin.py
from django.contrib import admin
from models import TextField, Book
class TextAdmin(admin.TabularInline):
model = TextField
class BookAdmin(admin.ModelAdmin):
inlines = [TextAdmin]
admin.site.register(Book, BookAdmin)

Categories

Resources