I have a model in my app that contains only one field - a ModelChoiceField - that I would like to be associated with user profiles (users being able to have multiple choices associated with them). These choices won't change while running, and I will eventually use them to filter the users by their choice. Here's what the two models look like.
...
class Choice(models.Model):
Choice1 = 0
Choice2 = 1
Choice3 = 2
Choices_Available = (
(Choice1, 'Choice1'),
(Choice2, 'Choice2'),
(Choice3, 'Choice3'),
)
choice = models.IntegerField(choices=Choices_Available, null=True)
def __iter__(self):
return iter(self.Choices_Available)
def __str__(self):
return self.Choices_Available[self.choice]
class Profile(models.Model):
choice = models.ManyToManyField(Choice)
The problem I have is trying to actually select a choice and associate that choice with the user. In the user form that would actually do the selecting, in most examples I have seen, a MultipleChoiceField with the available choice objects filtered:
...
class EditProfile(forms.ModelForm):
choice = forms.MultipleChoiceField(choices=Choice.objects.all(), widget=CheckboxSelectMultiple)
class Meta:
model = Profile
fields = ['choice']
That particular setup currently means nothing shows up because there are no Choice objects.
I think I would fix that by creating a choice object for each available choice. Then when a user requests that page with the form, they see the available choices. In the end, all users would be able to be associated with however many choice objects there are (in this example 3), and there would only be that many objects stored in the database minimizing space used.
To do that I need to be able to create Choice objects. My question boils down to: how do I create Choice objects with a choice selected (there currently is only the default init method). Also, is this a reasonable way of going about this feature?
Thanks
For anyone wondering the same thing, I'll tell you what I've found out.
To instantiate an object specifying the choice:
Choice.objects.create(choice=0)
Of course change the number corresponding to the choice, which can be done programmatically.
Also, I had a mistake in the form. MultipleChoiceField is for choosing from a set of options. ModelChoiceField is very similar, but it takes in a queryset of objects and allows you to chose from those objects. This is the functionality you might want for a many to many field like in my example.
Related
Forms can be complicated in Django. Formsets can make you want to quit Django. I'm at that point.
What are the different use cases and considerations of which one(s) to use?
I'm looking for some better guidance as to when to use each factory, as they seem to depend on when you know what type of form, fields, and whether or not you are creating, editing, or deleting (individual forms entirely or the parent model altogether). I have read many walkthroughs, but am struggling to see the larger picture, especially as I am attempting to move from function based views to Class Based Views.
Below are some pseudo code with assumptions/restrictions to help you help me understand the differences. It may help to provide psuedocode, such as what kind of Form (ModelForm or regular) goes with the Formset, or what should be popped from the form, given this seems to be a trend for creating forms with relations.
Assuming you have some models:
class Dish(models.Model):
name = models.CharField(max_length=50)
class Meal(models.Model):
name = models.CharField(max_length=50)
dishes = models.ManyToManyField(Dish,
# through='OPTIIONALMealDishIntermediaryClassTable',
related_name="meal")
class Reservation(models.Model):
date = models.DateTimeField()
greeting = models.CharField(max_length=255)
meal = models.OneToOneField(Meal, on_delete=models.CASCADE)
class MealPhotos(models.Model):
photo = models.OneToOneField(Photo, on_delete=models.CASCADE, related_name='mealPhoto')
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
# optional, so a photo can be attached to a dish if the picture is just of the dish
dish = models.ForeignKey(Dish, blank=True, null=True, on_delete=models.CASCADE)
And you want to create a new Meal, you want to send a Reservation at the same time:
which factory would you use?
does it depend on if the forms are all ModelForms? (meaning how would you handle assignming the Meal its Reservation)
assuming:
at this stage you know which Meal it is, but you still have to make a Reservation at the same time/same view.
you don't know which Dishes you are going to cook, since the reservation will tell you.
MealPhotos won't exist yet since the meal isn't prepared yet.
You want to create the meal and the reservation on the same form/screen
Then later, you want to add some dishes, based on what the Reservation says:
which factory would you use?
does it depend on if the forms are all ModelForms?
assuming:
at this stage you know which Meal it is, and you have a Reservation
you are going to assign dishes to the meal based on the Reservation, and you have enough information to do so, and can use a ModelForm easily, but not required
Later, the person eating the dish wants to take some photos, and you don't know how many they will take
which factory would you use?
does it depend on if the forms are all ModelForms?
assuming:
we will require them to take at least two
we have access to the Meal, Reservation, and Dishes
a photo could optionally be assigned to a Dish
The difference between the 3 formset factories is basically:
formset_factory lets you render a bunch of forms together, but these forms are NOT necessarily related to a particular database models (this is not what you need, since you have models for everything)
modelformset_factory lets you create/edit a bunch of Django model objects together, for example, if you were managing the "menu" you could use a modelformset_factory(Dish, fields=('name'))
inlineformset_factory lets you manage a bunch of Django model objects that are all related to a single instance of another model. For example, if you wanted to manage all of the MealPhotos that were taken at a particular Meal, this is what you would use
To answer your specific scenarios:
If you wanted a page that created a single Meal and a single Reservation at the same time, without assigning any Dishes yet, you don't need any formsets. You could just use a ModelForm for Meal and a ModelForm for Reservation.
Later on, if you want to attach multiple Dishes to the Meal, you would use an inlineformset_factory(Meal, Dish) to edit multiple Dishes belonging to a single Meal
Since we are using an inlineformset_factory, we have to create the Meal instance in the view that renders the form. Something like this:
DishFormSet = inlineformset_factory(Meal, Dish)
bday_dinner = Meal.objects.create(name='30th Birthday dinner')
formset = DishFormSet(instance=bday_dinner)
For someone uploading photos of the Meal, you would use:
PhotosFormSet = inlineformset_factory(Meal, MealPhotos)
bday_dinner = Meal.objects.get(name='30th Birthday dinner')
formset = PhotosFormSet(instance=bday_dinner)
This tells Django that all the photos submitted are all linked to that one Meal, but allows the possibility of assigning each photo to a different Dish (via a dropdown in the form).
Note: In the first scenario, I haven't tested whether you the use of a ManyToManyField as Meal.dishes is supported by the formset factory. If it isn't, then you could simply use a ModelFormset(Dish) and after those are created, link them to the Meal in the Django view that process the form submission.
I have a ModelForm for my Risk set up as:
class RiskForm(forms.ModelForm):
class Meta:
model = Risk
fields = '__all__'
def __init__(self, *args, **kwargs):
progid = kwargs.pop('progid')
super(RiskForm, self).__init__(*args,**kwargs)
dict_of_fields = {}
all_char = Program.objects.get(id=progid).char_set.all()
for char in all_char:
c = []
for cat in char.cat_set.all():
c.append( (cat.label, cat.label) )
dict_of_fields[char.label] = c
self.fields[char.label] = forms.ChoiceField(c)
Where the Risk Object is defined as:
class Risk(models.Model):
program = models.ForeignKey(Program, on_delete=models.CASCADE)
label = models.CharField(max_length=200, default='')
def __str__(self):
return self.label
However, I want to store the extra fields that I have created into my database under my Risk object.
As I have it now, it only stores the two attributes 'program' and 'label'. However, I also want to store the answers to the characteristics into my database for later usage.
For more information about how I've set things up: Django Form Based on Variable Attributes
And a print screen of my ModelForm: https://gyazo.com/89c9833613dbcc7e8d27cc23a3abaf72
Is it possible to store all 6 answers under my Risk Object in my database? If so, how do I do that?
A form in Django is a user interface object: it gives the user a set of fields to type answers into, checks that the data which they have supplied is valid, and converts it from text to the desired Python data-type (int, date, etc.). It does not have to relate to any particular model, and often doesn't. For example, an online shop is likely to have purchase selection forms which add data concerning possible orders into the user's session, rather than immediately performing any sort of update on a Product or Stock object. That happens later, at checkout.
A ModelForm assumes there is a Model to Form relationship. It is typically the right way to go when there is a simple relationship between the user's answers and a single model instance.
If you have a situation where the user's answers direct the creation of multiple database objects with a less direct relationship to any particular object, you probably don't want a ModelForm, just a Form. Also probably not a model-based view, but a function-based view. You can then do anything you need to between the view picking up the parameters from the URL parser, and displaying the form to the user. Likewise, anything between the view determining that the user's POST data is valid and telling the user whether his submitted request succeeded (or not, and why).
In this case I'm not clear how you want to store all six answers. If there's a predetermined fairly small set of answers you could have a single object with six? ten? possible sets of fields which are nullable to indicate that this object doesn't have that entity. Or, probably better, you could create a set of Answer objects each of which has a Foreign Key relationship to the Risk object, and later refer to Risk.answer_set (all the Answer objects which have a foreign key relationship to your risk object). This is open-ended, a Risk object can have anything from zero to bignum associated Answers.
I have created a model where I define a Place, which has several properties (M2M) and each property has several choices. A user is able to vote for one or more of the choices, therefore I define a M2M relationship from the choices to a user. However I do not achieve the required functionality, as the user is connected only to the choices and not to the specific place that has the choices. my model is as follows:
class Place(models.Model):
StoreName=models.CharField(max_length=200)
Pic=models.ImageField(upload_to="media",blank=True,null=True)
Address=models.CharField(max_length=200)
def __unicode__(self):
return self.StoreName
class TypeProperty(models.Model):
Place = models.ManyToManyField(Place)
TypePropertyName=models.CharField(max_length=42)
def __unicode__(self):
return self.TypePropertyName
class TypeChoices(models.Model):
TypeProperty = models.ForeignKey(TypeProperty)
TypeChoiceName=models.CharField(max_length=42)
UserVoted=models.ManyToManyField(User,blank=True,null=True)
How can I achieve the functionality
A User has voted for the Choice that for the specific Place
From User you can get TypeChoices:
user_instance.typechoices_set.all()
Then, you can loop through each TypeChoice and access the Place queryset through the TypeProperty attribute:
typechoice_instance.TypeProperty.Place.all()
Then you would need to loop through each Place to do whatever.
Now, looking at that, it should be immediately apparent that there's some serious flaws here. The code makes no sense, and it's not readable. This is why coding conventions exist. Please give the Python Style Guide (PEP8) a thorough read.
In particular to your code here, attributes on a class should be all lower-case and words in the attribute's name should be separated by underscores, i.e. store_name NOT StoreName.
Then, attribute names should parallel what they represent: Place makes no sense for a field that will return multiple places. Name it places to indicate that it returns multiple items (and in particular returns a queryset rather than a model instance).
UPDATE
If you want that, then you need to work backwards and select the TypeChoices:
TypeChoices.objects.filter(UserVoted=some_user, TypeProperty__Place=some_place)
I posted this question on the django-users list, but haven't had a reply there yet.
I have models that look something like this:
class ProductGroup(models.Model):
name = models.CharField(max_length=10, primary_key=True)
def __unicode__(self): return self.name
class ProductRun(models.Model):
date = models.DateField(primary_key=True)
def __unicode__(self): return self.date.isoformat()
class CatalogItem(models.Model):
cid = models.CharField(max_length=25, primary_key=True)
group = models.ForeignKey(ProductGroup)
run = models.ForeignKey(ProductRun)
pnumber = models.IntegerField()
def __unicode__(self): return self.cid
class Meta:
unique_together = ('group', 'run', 'pnumber')
class Transaction(models.Model):
timestamp = models.DateTimeField()
user = models.ForeignKey(User)
item = models.ForeignKey(CatalogItem)
quantity = models.IntegerField()
price = models.FloatField()
Let's say there are about 10 ProductGroups and 10-20 relevant
ProductRuns at any given time. Each group has 20-200 distinct
product numbers (pnumber), so there are at least a few thousand
CatalogItems.
I am working on formsets for the Transaction model. Instead of a
single select menu with the several thousand CatalogItems for the
ForeignKey field, I want to substitute three drop-down menus, for
group, run, and pnumber, which uniquely identify the CatalogItem.
I'd also like to limit the choices in the second two drop-downs to
those runs and pnumbers which are available for the currently
selected product group (I can update them via AJAX if the user
changes the product group, but it's important that the initial page
load as described without relying on AJAX).
What's the best way to do this?
As a point of departure, here's what I've tried/considered so far:
My first approach was to exclude the item foreign key field from the
form, add the substitute dropdowns by overriding the add_fields
method of the formset, and then extract the data and populate the
fields manually on the model instances before saving them. It's
straightforward and pretty simple, but it's not very reusable and I
don't think it is the right way to do this.
My second approach was to create a new field which inherits both
MultiValueField and ModelChoiceField, and a corresponding
MultiWidget subclass. This seems like the right approach. As
Malcolm Tredinnick put it in
a django-users discussion,
"the 'smarts' of a field lie in the Field class."
The problem I'm having is when/where to fetch the lists of choices
from the db. The code I have now does it in the Field's __init__,
but that means I have to know which ProductGroup I'm dealing with
before I can even define the Form class, since I have to instantiate the
Field when I define the form. So I have a factory
function which I call at the last minute from my view--after I know
what CatalogItems I have and which product group they're in--to
create form/formset classes and instantiate them. It works, but I
wonder if there's a better way. After all, the field should be
able to determine the correct choices much later on, once it knows
its current value.
Another problem is that my implementation limits the entire formset
to transactions relating to (CatalogItems from) a single
ProductGroup.
A third possibility I'm entertaining is to put it all in the Widget
class. Once I have the related model instance, or the cid, or
whatever the widget is given, I can get the ProductGroup and
construct the drop-downs. This would solve the issues with my
second approach, but doesn't seem like the right approach.
One way of setting field choices of a form in a formset is in the form's __init__ method by overwriting the self.fields['field_name'].choices, but since a more dynamic approach is desired, here is what works in a view:
from django.forms.models import modelformset_factory
user_choices = [(1, 'something'), (2, 'something_else')] # some basic choices
PurchaserChoiceFormSet = modelformset_factory(PurchaserChoice, form=PurchaserChoiceForm, extra=5, max_num=5)
my_formset = PurchaserChoiceFormSet(self.request.POST or None, queryset=worksheet_choices)
# and now for the magical for loop
for choice_form in my_formset:
choice_form.fields['model'].choices = user_choices
I wasn't able to find the answer for this but tried it out and it works in Django 1.6.5. I figured it out since formsets and for loops seem to go so well together :)
I ended up sticking with the second approach, but I'm convinced now that it was the Short Way That Was Very Long. I had to dig around a bit in the ModelForm and FormField innards, and IMO the complexity outweighs the minimal benefits.
What I wrote in the question about the first approach, "It's straightforward and pretty simple," should have been the tip-off.
This is a problem concerning django.
I have a model say "Automobiles". This will have some basic fields like "Color","Vehicle Owner Name", "Vehicle Cost".
I want to provide a form where the user can add extra fields depending on the automobile that he is adding. For example, if the user is adding a "Car", he will extra fields in the form, dynamically at run time, like "Car Milage", "Cal Manufacturer".
Suppose if the user wants to add a "Truck", he will add "Load that can be carried", "Permit" etc.
How do I achieve this in django?
There are two questions here:
How to provide a form where the user can add new fields at run time?
How to add the fields to the database so that it can be retrieved/queried later?
There are a few approaches:
key/value model (easy, well supported)
JSON data in a TextField (easy, flexible, can't search/index easily)
Dynamic model definition (not so easy, many hidden problems)
It sounds like you want the last one, but I'm not sure it's the best for you. Django is very easy to change/update, if system admins want extra fields, just add them for them and use south to migrate. I don't like generic key/value database schemas, the whole point of a powerful framework like Django is that you can easily write and rewrite custom schemas without resorting to generic approaches.
If you must allow site users/administrators to directly define their data, I'm sure others will show you how to do the first two approaches above. The third approach is what you were asking for, and a bit more crazy, I'll show you how to do. I don't recommend using it in almost all cases, but sometimes it's appropriate.
Dynamic models
Once you know what to do, this is relatively straightforward. You'll need:
1 or 2 models to store the names and types of the fields
(optional) An abstract model to define common functionality for your (subclassed) dynamic models
A function to build (or rebuild) the dynamic model when needed
Code to build or update the database tables when fields are added/removed/renamed
1. Storing the model definition
This is up to you. I imagine you'll have a model CustomCarModel and CustomField to let the user/admin define and store the names and types of the fields you want. You don't have to mirror Django fields directly, you can make your own types that the user may understand better.
Use a forms.ModelForm with inline formsets to let the user build their custom class.
2. Abstract model
Again, this is straightforward, just create a base model with the common fields/methods for all your dynamic models. Make this model abstract.
3. Build a dynamic model
Define a function that takes the required information (maybe an instance of your class from #1) and produces a model class. This is a basic example:
from django.db.models.loading import cache
from django.db import models
def get_custom_car_model(car_model_definition):
""" Create a custom (dynamic) model class based on the given definition.
"""
# What's the name of your app?
_app_label = 'myapp'
# you need to come up with a unique table name
_db_table = 'dynamic_car_%d' % car_model_definition.pk
# you need to come up with a unique model name (used in model caching)
_model_name = "DynamicCar%d" % car_model_definition.pk
# Remove any exist model definition from Django's cache
try:
del cache.app_models[_app_label][_model_name.lower()]
except KeyError:
pass
# We'll build the class attributes here
attrs = {}
# Store a link to the definition for convenience
attrs['car_model_definition'] = car_model_definition
# Create the relevant meta information
class Meta:
app_label = _app_label
db_table = _db_table
managed = False
verbose_name = 'Dynamic Car %s' % car_model_definition
verbose_name_plural = 'Dynamic Cars for %s' % car_model_definition
ordering = ('my_field',)
attrs['__module__'] = 'path.to.your.apps.module'
attrs['Meta'] = Meta
# All of that was just getting the class ready, here is the magic
# Build your model by adding django database Field subclasses to the attrs dict
# What this looks like depends on how you store the users's definitions
# For now, I'll just make them all CharFields
for field in car_model_definition.fields.all():
attrs[field.name] = models.CharField(max_length=50, db_index=True)
# Create the new model class
model_class = type(_model_name, (CustomCarModelBase,), attrs)
return model_class
4. Code to update the database tables
The code above will generate a dynamic model for you, but won't create the database tables. I recommend using South for table manipulation. Here are a couple of functions, which you can connect to pre/post-save signals:
import logging
from south.db import db
from django.db import connection
def create_db_table(model_class):
""" Takes a Django model class and create a database table, if necessary.
"""
table_name = model_class._meta.db_table
if (connection.introspection.table_name_converter(table_name)
not in connection.introspection.table_names()):
fields = [(f.name, f) for f in model_class._meta.fields]
db.create_table(table_name, fields)
logging.debug("Creating table '%s'" % table_name)
def add_necessary_db_columns(model_class):
""" Creates new table or relevant columns as necessary based on the model_class.
No columns or data are renamed or removed.
XXX: May need tweaking if db_column != field.name
"""
# Create table if missing
create_db_table(model_class)
# Add field columns if missing
table_name = model_class._meta.db_table
fields = [(f.column, f) for f in model_class._meta.fields]
db_column_names = [row[0] for row in connection.introspection.get_table_description(connection.cursor(), table_name)]
for column_name, field in fields:
if column_name not in db_column_names:
logging.debug("Adding field '%s' to table '%s'" % (column_name, table_name))
db.add_column(table_name, column_name, field)
And there you have it! You can call get_custom_car_model() to deliver a django model, which you can use to do normal django queries:
CarModel = get_custom_car_model(my_definition)
CarModel.objects.all()
Problems
Your models are hidden from Django until the code creating them is run. You can however run get_custom_car_model for every instance of your definitions in the class_prepared signal for your definition model.
ForeignKeys/ManyToManyFields may not work (I haven't tried)
You will want to use Django's model cache so you don't have to run queries and create the model every time you want to use this. I've left this out above for simplicity
You can get your dynamic models into the admin, but you'll need to dynamically create the admin class as well, and register/reregister/unregister appropriately using signals.
Overview
If you're fine with the added complication and problems, enjoy! One it's running, it works exactly as expected thanks to Django and Python's flexibility. You can feed your model into Django's ModelForm to let the user edit their instances, and perform queries using the database's fields directly. If there is anything you don't understand in the above, you're probably best off not taking this approach (I've intentionally not explained what some of the concepts are for beginners). Keep it Simple!
I really don't think many people need this, but I have used it myself, where we had lots of data in the tables and really, really needed to let the users customise the columns, which changed rarely.
Database
Consider your database design once more.
You should think in terms of how those objects that you want to represent relate to each other in the real world and then try to generalize those relations as much as you can, (so instead of saying each truck has a permit, you say each vehicle has an attribute which can be either a permit, load amount or whatever).
So lets try it:
If you say you have a vehicle and each vehicle can have many user specified attributes consider the following models:
class Attribute(models.Model):
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
attribute = models.ManyToMany(Attribute)
As noted before, this is a general idea which enables you to add as much attributes to each vehicle as you want.
If you want specific set of attributes to be available to the user you can use choices in the Attribute.type field.
ATTRIBUTE_CHOICES = (
(1, 'Permit'),
(2, 'Manufacturer'),
)
class Attribute(models.Model):
type = models.CharField(max_length=1, choices=ATTRIBUTE_CHOICES)
value = models.CharField()
Now, perhaps you would want each vehicle sort to have it's own set of available attributes. This can be done by adding yet another model and set foreign key relations from both Vehicle and Attribute models to it.
class VehicleType(models.Model):
name = models.CharField()
class Attribute(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
attribute = models.ManyToMany(Attribute)
This way you have a clear picture of how each attribute relates to some vehicle.
Forms
Basically, with this database design, you would require two forms for adding objects into the database. Specifically a model form for a vehicle and a model formset for attributes. You could use jQuery to dynamically add more items on the Attribute formset.
Note
You could also separate Attribute class to AttributeType and AttributeValue so you don't have redundant attribute types stored in your database or if you want to limit the attribute choices for the user but keep the ability to add more types with Django admin site.
To be totally cool, you could use autocomplete on your form to suggest existing attribute types to the user.
Hint: learn more about database normalization.
Other solutions
As suggested in the previous answer by Stuart Marsh
On the other hand you could hard code your models for each vehicle type so that each vehicle type is represented by the subclass of the base vehicle and each subclass can have its own specific attributes but that solutions is not very flexible (if you require flexibility).
You could also keep JSON representation of additional object attributes in one database field but I am not sure this would be helpfull when querying attributes.
Here is my simple test in django shell- I just typed in and it seems work fine-
In [25]: attributes = {
"__module__": "lekhoni.models",
"name": models.CharField(max_length=100),
"address": models.CharField(max_length=100),
}
In [26]: Person = type('Person', (models.Model,), attributes)
In [27]: Person
Out[27]: class 'lekhoni.models.Person'
In [28]: p1= Person()
In [29]: p1.name= 'manir'
In [30]: p1.save()
In [31]: Person.objects.a
Person.objects.aggregate Person.objects.all Person.objects.annotate
In [32]: Person.objects.all()
Out[33]: [Person: Person object]
It seems very simple- not sure why it should not be a considered an option- Reflection is very common is other languages like C# or Java- Anyway I am very new to django things-
Are you talking about in a front end interface, or in the Django admin?
You can't create real fields on the fly like that without a lot of work under the hood. Each model and field in Django has an associated table and column in the database. To add new fields usually requires either raw sql, or migrations using South.
From a front end interface, you could create pseudo fields, and store them in a json format in a single model field.
For example, create an other_data text field in the model. Then allow users to create fields, and store them like {'userfield':'userdata','mileage':54}
But I think if you're using a finite class like vehicles, you would create a base model with the basic vehicle characteristics, and then create models that inherits from the base model for each of the vehicle types.
class base_vehicle(models.Model):
color = models.CharField()
owner_name = models.CharField()
cost = models.DecimalField()
class car(base_vehicle):
mileage = models.IntegerField(default=0)
etc