Creating Dynamic Forms in Django - django

I'm working on a project that involves a form with some standard fields and some custom field users define later. The standard forms are defined on a model in models.py. For example:
class Order(models.model):
number = models.TextField()
date = models.DateField()
I then use this model to create a simple model form to make a way to fill in the information. That's pretty standard Django.
The tricky thing is that my users want to be able to add arbitrary fields to the form. They would like to be able to use the Admin interface to basically modify the form and add values to it at run time.
So, they might want a new text field called "Tracking Number" or something like that. The trick is that they only need it sometimes and they want to be able to add it dynamically without rebuilding the whole database.
I can create a fairly simple model to represent the custom fields like so:
class CustomField(models.Model):
type = models.CharField(choices=FIELD_TYPES)
required = models.BooleanField()
I think I can then take the ModelForm for the Order class and extend it to add these custom fields. What I am unsure of is how to link the custom field values back to the Order.
I know this all might sound odd, but in practice it makes sense. Each user has slightly different needs for the form and want to tweak it. If I have to hard code the models to have their specific fields, then I will have to have a fork for each user. That simply doesn't scale. If instead they can simply add the fields through the admin interface, then things are much simpler.
I feel like this is something that is perhaps already solved by someone out there. I simply cannot find a solution. I can't be the only one who has gotten this kind of request right?

Related

Create fields based on another model

I have a User model with some fields. Some of them will require feedback, are they correctly filled (if not, specific message will be displayed on user profile).
The problem is, how to represent 'invalid' fields in database. My idea is to create another model (call it ExtUser) with OneToOneField to User. And ExtUser should have same fields' names as User, but their types will be all boolean, determining whether field is filled in correctly. For example, if User has a field called email:
email = models.CharField(max_length=100)
ExtUser would have following field:
email = models.BooleanField(default=False)
Here's a problem with this approach. How am I supposed to create fields in ExtUser? Of course I can create them manually, but that would be breaking of DRY principle, and I'm not going to do that. The question is, can I add fields to model dynamically, and have them in database (so I assume it would require to be called before migrate)?
I have django 1.8 and I don't want to use any external modules/libraries.
If someone has an another idea of how to represent that data in database, please add comment, not a reply - as this question is about creating fields dynamically.
You will need to do this manually.
Python does not disallow this behavior; you can take a look at this SO response on dynamically created classes, but Django will not be able to interpret the output. In particular, Django relies on the models to create the SQL tables for the application, and there is essentially no way for this to occur if you model is not statically defined.
In this case, I don't think you have to worry much about DRY; if you need a separate model with fields which happen to be related to, but different from, another model, I think it's probably ok.
Finally, I'm unsure what your goal is, but you could probably define some functions which can determine how "correct" the fields of the user are. This is how I would recommend solving this problem (if it applies).

implementing multiple profiles with django-all-auth

Django noob here - I was recently pointed to django-all-auth for registering/handling users, which I'm finding awesome in its near instant setup.
However I'm stumbling at trying to implement multiple user profile models. In reading other answers I've found this to be the closest answer thus far, but not really what I need.
When coding my own basic registration the user would select an account type (basic, pro, elite for example - each being their own profile model). Depending on the link selected the signup form would display both the generic User registration form as well as the profile form of the type chosen by the user.
I know I can go so far as to completely customize all-auth and make something like this work, but I'm hoping to be pointed in a direction that involves less wrecking of the original app. I've thought about having user redirected after signup to choose a profile type, but that seems to be a lot of extra steps.
Thanks for the help!
To extend the basic user class, just subclass AbstractUser. You can find that in the docs. With this you can add the fields your basic user is missing.
Now, you want several types of profiles with different fields, or perhaps the same fields but adding new fields every time.
You can create something like:
class ProfileBase(models.Model):
user=models.OneToOneField(User)
class ProfilePro(ProfileBase):
pro_field=models.SomeField(Foo)
#You can extend ProfilePro here if you want it to include the pro_field
class ProfileElite(ProfileBase):
elite_field=models.someField(Bar)
Once you have these models creating the forms should be easy.
Be aware, when you subclass this way django creates 1 table per model, including on the subclass table only the new fields. This makes necessary a join per level of inheritance so try not to abuse that.
There is a second way to use inheritance:
class ProfileBase(models.Model):
user=models.OneToOneField(User)
class Meta:
abstract=True
If you define the base class as abstract you won't have a table for it, so you can't create objects for that model, but each of your subclasses will be on it's own table. If you do it this way you may need extra logic to handle cases when user changes of type of profile (i.e. went from pro to elite).
To keep this separated from django-allauth, just finish the registration (create a form and in your settings define ACCOUNT_SIGNUP_FORM_CLASS to override all-auth default with your basic info + pick a profile type form) and once your user is logged in, redirect them to their profile to finish the process.

Django Model field as formfield in templates

I guess this is a simple question, but I am not being able to find a definitive answer whether this is possible or not.
I have a Model object that is being passed to my template during rendering. While I use most of the Model fields to show their values, I came across a need to show a few of them as formfields (with their respective HTML element like rendered via ModelForms). Can ModelForm class usage be avoided here and simply use the Model's field to be rendered as formfield directly from template?
an example:
class MyModel(models.Model):
month = models.PositiveIntegerField(_('Month'), choices=MONTH_CHOICES)
year = models.PositiveIntegerField(_('Year'))
Now in template based on the above Model example I want to show the year as a value but month as a dropdown with selected current value?
Any pointer appreciated!
Use case:
Imagine a generic list_view like case where you are rendering a queryset. What you have there is object per iteration, however on the list view you want to allow your users to perform some quick editing of few object attributes to avoid forcing them to go to full edit mode (maybe via simple ajax).
No.
As canvassed in the comments, this is not possible. A model, and its fields do not know about forms - that is what forms, and modelforms are for.
The right way to do this is to wrap your model objects with a modelform, which you can adjust (using inheritance) to do whatever you want with the non-form fields, if you wish to make this more transparent.
Creating modelforms in the template means that you have nowhere to process the forms in django. Apparently you want to interface them with some kind of ajax interface, so perhaps this is not a problem for you, but in any case, I fail to see how it would be easier than simply creating the modelforms in the view.

Is it possible for Django ModelForms to work with dynamically added fields?

I've managed to get Django Forms to dynamically generate additional fields based on the relationship between a specific instance (eg. 'product type') and another model (eg. 'product attributes') eg. products have common attributes like weight and price but a book has a page count and a computer has specs.
I'd like to be able to do the same with ModelForms so that I can just call form.save() but I'm not sure what the right approach would be to do this or where to start. At first I thought it would be possible by overriding some of the methods but then I've looked around the models.py file and it seems that I'd need to add quite a bit of code at various places in there to handle the additional fields ie. quite a lot of work. Or am I missing the easy way?
Without knowing the specifics, I'd say you're right. If the additional fields are not part of the model, then at the least, you'd have to override the ModelForm's save() method.

How can I use django forms/models to represent choices between fields?

How can I use boolean choices in a model field to enable/disable other fields. If a boolean value is true/false I want it to enable/disable other model fields. Is there a way to natively express these relationships using django models/forms/widgets? I keep writing custom templates to model these relationships, but can't figure out a good way to represent them in django without a special template.
For example:
class PointInTime(models.Model):
is_absolute_time = models.BooleanField()
absolute_time = models.DateTimeField()
is_relative_time = models.BooleanField()
days_before = models.IntegerField()
So if the is_absolute_time is True, I want the absolute_time entry to be editable in the GUI and the days_before entry to be grayed out and not-editable. If the 'is_relative_time' flag is True, I want the absolute_time entry to be grayed out, and the days_before value to be editable. So is_absolute_time and is_relative_time would be radio buttons in the same Group in the GUI and their two corresponding fields would only be editable when their radio button is selected. This is easy to do in a customized template, but is there a way to use a model/form in django to natively show this relationship?
It would be helpful to clarify what you mean by "natively show this relationship," and think clearly about separation of concerns.
If all you want is to "gray out" or disable a certain field based on the value of another field, this is purely a presentation/UI issue, so the template (and/or Javascript) is the appropriate place to handle it.
If you want to validate that the submitted data is internally consistent (i.e. absolute_time is filled in if is_absolute_time is True, etc), that's a form-validation issue. The place for that logic is in the clean() method of your Form or ModelForm object.
If you want to ensure that no PointInTime model can ever be saved to the database without being internally consistent, that's a data-layer concern. The place for that is in a custom save() method on your model object (Django 1.2 will include a more extensive model validation system).
All of those options involve writing imperative code to do what you need with these specific fields. It may be that you're looking for a way to represent the situation declaratively in your model so that the code in all three of the above cases can be written generically instead of specifically. There's no built-in Django way to do this, but you could certainly do something like:
class PointInTime(models.Model):
field_dependencies = {'is_absolute_time': 'absolute_time',
'is_relative_time': 'days_before'}
... fields here ...
Then your model save() code (or your Form clean() code, or your template), could use this dictionary to determine which fields should be enabled/disabled depending on the value of which other one. This generalization is hardly worth the effort, though, unless you anticipate needing to do this same thing in a number of different models.
Lastly, a few schema design alternatives you may want to consider to get your data layer better normalized:
If there are only two valid states (absolute and relative), use a single boolean field instead of two. Then you avoid possible inconsistencies (what does it mean if both booleans are False? Or True?)
Or simplify further by eliminating the booleans entirely and just using Null values in one or the other of absolute_time/days_before.
If there might be more than two valid states, use a single IntegerField or CharField with choices instead of using two boolean fields. Same reason as above, but can accomodate more than two options.
Since a RelativeTime and an AbsoluteTime don't appear to share any data fields with each other, consider splitting them out into separate models entirely. If you have other models that need a ForeignKey to either one or the other, you could model that with inheritance (both RelativeTime and AbsoluteTime inherit from PointInTime, other models have ForeignKeys to PointInTime).
I'm not entirely sure what you're doing with these objects but whichever the user chooses, you're pointing to a single moment in time. "5 days ago" is "Thursday" and vice-versa.
So unless the dates roll with the site (eg the record with "5 days ago" will still mean Thursday, tomorrow, etc), surely this is only an interface problem? If that's the case, I'd stick with a single value for the date in your Model and let the form and view do all the work.
That solves the auto-generated Admin side of things as you'll just have one field to contend with but it won't natively give you the choice between the two unless you write your own form widget and override the ModelAdmin class for your Model.
If this isn't the case, please ignore this answer.