Django Dynamic Fields Within A Form - python

A requirement for my app is to give user's capability to create a survey. For each survey, the user should have the capability to add any number of questions. I am trying to achieve this by first defining my models and a form.
# models.py.
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
def __str__(self):
return self.question_text
class Survey(models.Model):
survey_name = models.CharField(max_length=200)
questions = models.ForeignKey(Question, on_delete=models.CASCADE)
def __str__(self):
return self.survey_name
#forms.py
from django import forms
class Survey(forms.Form):
survey_name = forms.CharField(required=200)
#TODO: define questions
I am stuck. In my form module, how do I define the one to many relationship between the survey and questions in order for the user to add and define questions for each survey they create.

You can follow this tutorial to make dynamic forms dynamic form using django
or you can use javascript dynamic input using javascript to let user creat as many field wish. and get those field on view using request.POST.getlist("html input field name") and then insert onto model.

Related

Django - Create a User instance from another model that uses User as a OneToOne

I think an example will explain it better.
Lets say I am making an application for various Libraries to show what books they have available.
The site will have Users that are registered through the built-in user registration system and User model. These Users (think of it like library members) can visit the Libraries and browse and checkout the available books.
The site will also allow Libraries to register themselves, and part of that registration is declaring a "Librarian," a person who controls what books are available in their particular Library.
I want to create a Library Registration Form that takes the information of this Library and its Librarian and not only creates an instance of the Library model, but also an instance of the User model. The Librarian automatically becomes a User.
This is my current models.py:
class Library(models.Model):
name = models.CharField(max_length=200)
website = models.URLField()
street = models.CharField(max_length=200)
city = models.CharField(max_length=200)
state_or_province = models.CharField(max_length=200)
postal_code = models.CharField(max_length=200)
date_registered = models.DateField(auto_now_add=True)
librarian = models.OneToOneField(User, on_delete=models.CASCADE)
#receiver(post_save, sender=Library)
def create_user(sender, instance, created, **kwargs):
if created:
User.objects.create(user=instance)
instance.user.save()
I am currently lost as to how to build views.py and forms.py. I am not even sure that model is built correctly, since I need the form to include not only the Library information, but also User information (first_name, last_name, email, password...). Do I need to duplicate that information in the Library model in order for it to pass to the form?
Basically, I don't have a good grasp of how to models connect to one another via Django tools and files. If anyone can point me in the right direction, I would appreciate it. Thanks!
You can do this with standard django, however it is quite long.
Or you can use django-extra-views, to make your life nice and easy.
class LibrarianInline(GenericInlineFormSet):
model = User
fields = '__all__'
class LibraryInline(CreateWithInlinesView):
model = Library
inlines = [LibrarianInline]
fields = '__all__'
There is also a simpler way of doing it with standard django. Force the librarian to be created first and only then allow them to create a Library.
urls.py
urlpatterns = [
url(r'^/create-librarian$',
LibrarianCreateView.as_view(), name='create_librarian'),
url(r'^/create-library/for/(?P<librarian_id>\d+)$',
LibraryCreateView.as_view(), name='create_library'),
]
views.py
from django.shotcuts import reverse
from django.generic.views import CreateView
class LibrarianCreateView(CreateView):
model = User
def form_valid(self, form):
librarian = form.save(commit=True)
return redirect('create_library', {'librarian_id': librarian.id})
class LibraryCreateView(CreateView):
model = Library
def form_valid(self, form):
library = form.save(commit=False)
librarian_id = self.kwargs['librarian_id']
# You can do validation here if you fancy
library.librarian_id = librarian_id
library.save()
return self.get_success_url()
By requiring the id of the Librarian to create the Library, it prevents it being created without a librarian.

How to connect models from different apps in Django?

I have a few apps within my Django project. There are two apps that I am currently working with "Application" and "User" and I have two questions related to models:
Question 1:
I want to design it in such a way so that external users submit their contact form on Application/templates/Application/Apply.html and the info would get added to the database. Internal users would be able to add external users as well but from a different template: User/templates/User/AddNewContact.html
I am able to add a new contact from an internal user's perspective:
User/models.py
class Contact(models.Model):
ContactName = models.CharField(max_length = 250, default='')
ContactResidence = models.CharField(max_length = 250, default='')
Tel = models.CharField(max_length = 250, default='')
def get_absolute_url(self):
return reverse('User:ContactDetails', kwargs={'pk': self.pk}
)
def __str__(self):
return self.ContactName
class Locations(models.Model):
contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
Country = models.CharField(max_length=250, default='')
def __str__(self):
return self.Country
I was going to just copy this model and paste it into Application/models.py but there are two problems:
1) I don't want external users to be directed to URL: User:ContactDetails and technically, it is not going to work out because I will build the authentication later on.
2) I feel that by copying and pasting I am breaking the 'don't repeat yourself" rule.
Should I connect two models using the foreign keys? What are the best practices in this case?
Question 2
Am I working with one-to-many relationship according to the model provided? I want to have one contact with his personal info (tel/email/address) and a number of branch locations across the world associated with that contact.
To be used a relationship one to many, you can be doing as after:
On models of father app (father table):
class Department(models.Model):
dept_id = models.AutoField(primary_key=True)
On models of child app (child table):
from appname.models import Department
class Office(models.Model):
office_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
dept = models.ForeignKey(Department, on_delete=models.CASCADE)
It helped me.
Question 1: Well, you don't need to copy paste the model. You can use models from other django apps anytime, just need to import it. Basically what you should do is, instead of linking the url directly to the template in the Applications app, you should connect it to a view. In the view file you can import the models from User.models import *, and use them normally.
Question 2: As far as I understand the question your structure provides what you want: one contact (with personal info) associated with several countries. Except that you should replace Agent by Contact in contact = models.ForeignKey(Agent, on_delete=models.CASCADE)
Question 1: Note that the 'get_absolute_url' method is only called if you don't provide a success url in your view. If you are using a CreateView or FormView you can specify the success url by overriding the get_success_url method, for example:
class ContactCreateView(CreateView):
model = Contact
fields = ['ContactName', 'ContactResidence', 'Tel']
def get_success_url(self):
if not self.request.user.internal: # e.g. internal is a User bool field
return HttpResponseRedirect('some/external/url/')
return super().get_success_url() # call get_absolute_url model method.
The DRY principle is respected.
Question 2: Yes, the question you need to ask yourself is 'does a model instance (In this case Contact) have many instances of another model (Location)?' If the answer is yes, then the M2M field should go into your Contact model. See the django docs explaining the pizza/toppings example.
The apps should be in the same project and you can import one model as:
import appName.models or
from appName.models import ClassName
In app2 models you can use foreignKey or manyTomany after importing the class:
from appsName.models import ClassName
class Person(models.Model):
con = ForeignKey(ClassName)

Django: combine two ForeignKeys into one field

I need to implement the following:
The user shall be presented with a form that will have a drop down choice menu consisting of property names. There are two types of properties: general properties, i.e. properties common for all users and custom properties, i.e. properties that each user has defined prior to that. The models would look something like that:
class GeneralPropertyName(models.Model):
name = models.CharField(max_length=20)
class CustomPropertyName(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=20)
The drop down menu should have all general properties and only those custom properties that pertain to the user.
First question: how to define such a model?
I need to: 1. somehow unify both properties, 2. take only those items from CustomPropertyName that pertain to the user
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(GeneralPropertyName) ??UNIFY??? ForeignKey(CustomPropertyName)
Second, is there anything special that needs to be done with ModelForm?
class SpecDataForm(ModelForm):
class Meta:
model = SpecData
And the 3rd question is what needs to be done in the view? I will need to use inline formsets since I will have a few dynamic forms like that.
def index(request):
user = User.objects.get(username=request.user.username)
specdataFormSet = inlineformset_factory(User, SpecData, form=SpecDataForm, extra=30)
...
specdata_formset = specdataFormSet(instance=user, prefix='specdata_set')
...
Thanks.
EDIT: Adjusted juliocesar's suggestion to include formsets. Somehow I am getting the following error message: Cannot resolve keyword 'property' into field. Choices are: id, name, selection_title, user
def index(request):
user = User.objects.get(username=request.user.username)
user_specdata_form = UserSpecDataForm(user=user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
You can use a GenericForeignKey to handle it, but you still need more to solve your further questions about forms and view.
I have made an example of how you solve your problem (logged user can select from General properties and his Custom properties, non-logged user only can select General properties). I used model inheritance for the properties (In your sample code it seems that a CustomPropertyName is a PropertyName with other fields). I think inheritance is an easier and a more basic concept than ContentTypes and it fits to your needs.
NOTE: I remove some code like imports to simplify the code.
1) models.py file:
class PropertyName(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class CustomPropertyName(PropertyName): # <-- Inheritance!!
user = models.ForeignKey(User)
def __unicode__(self):
return self.name
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = models.ForeignKey(PropertyName)
NOTES: The field SpecData.property points to PropertyName since all properties are saved in the PropertyName's database table.
2) forms.py file:
from django import forms
from django.db.models import Q
from models import SpecData, PropertyName
def UserSpecDataForm(user=None):
UserPropertiesQueryset = PropertyName.objects.filter(Q(custompropertyname__user=None) | Q(custompropertyname__user__id=user.id))
class SpecDataForm(forms.ModelForm):
property = forms.ModelChoiceField(queryset=UserPropertiesQueryset)
class Meta:
model = SpecData
exclude = ('user',)
return SpecDataForm
NOTES: The trick here is to generate the form SpecDataForm dynamically, by filtering properties according the user specified in the parameter.
3) views.py file:
from forms import UserSpecDataForm
def index(request):
if request.POST:
form = UserSpecDataForm(request.user)(request.POST) # instance=user
if form.is_valid():
spec_data = form.save(commit=False)
spec_data.user = request.user
spec_data.save()
else:
form = UserSpecDataForm(request.user)()
return render_to_response('properties.html', {'form': form}, context_instance=RequestContext(request))
NOTES: Nothing special here, just a call to form.UserSpecDataForm(request.user) that returns the form class and then instantiate. Also setted the logged-in user to the object returned on save since It was excluded in the form to not show in front-end.
Following this basic example you can do the same with formsets if you need it.
UPDATE:
Formset can be used by adding following code to the view:
user_specdata_form = UserSpecDataForm(user=request.user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
The complete project sample can be downloaded from http://ge.tt/904Wg7O1/v/0
Hope this helps
1a) have you looked into django's ContentType framework this will allow you to have generic foreign keys and you can put restrictions on what types of models are acceptable to store in.
1b) I think that the validation for accepting what type of foreign key is acceptable shouldn't be in your model but should be part of your form validation before saving.
2) If you do use a model form you're going to have to define your own custom widget for the propery field. This means you're probably going to have to write you're own render function to render the html from the field. You should also define your own validation function on the form to make sure that only the appropriate data is acceptable to save.
3) I don't think you'll have to do anything you aren't already doing in the views
Use GenericForeignKey:
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
property = GenericForeignKey('content_type', 'object_id')
You can use this to combine the two fields(type & id) into a single choice field.
One way is that you have only one model, make user nullable:
class PropertyName(models.Model):
user = models.ForeignKey(User, null=True, blank=True)
name = models.CharField(max_length=20)
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(PropertyName)
So, if user is not set, it is a general property. If it is set, it is related to this user.
However, please note that if you need unique property names, that NULL != NULL.
Of course, the suggested GenericForeignKey solution is better for some cases.
Also, you can easily make the normal (non-model) form with that you describe and separate form logic from model logic.

Django Form based on multiple models

If I have two models in Django application like this:
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author)
title = models.CharField(max_length=100)
How can I create a single form that allows you add both an Author and a Book simultaneously. If the author exists in the system, I could simply display the book Form and link that to the author but it is very often that I need to allow my users to create the book and the author simultaneously.
How can I do this?
Thanks.
You can write a custom form, which will check if the author exists in the system use existing, if no, create new with provided name.
class CustomForm(forms.ModelForm):
author = forms.CharField()
def save(self, commit=True):
author, created = Author.objects.get_or_create(name=self.cleaned_data['author'])
instance = super(CustomForm,self).save(commit=commit)
instance.author = author
if commit:
instance.save()
return instance
class Meta:
model=Book
Not sure this code is working, but I suppose it can explain my idea.
You can create a view that handles multiple forms - see http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ for an excellent example.
You'd have to ensure that the rendering of the form objects are done in the template with only one tag and one submit button.

Django - Working with multiple forms

What I'm trying to do is to manage several forms in one page, I know there are formsets, and I know how the form management works, but I got some problems with the idea I have in mind.
Just to help you to imagine what my problem is I'm going to use the django example models:
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField()
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
Now, imagine I've already made the form clases:
from django import forms
from mysite.polls.models import Poll, Choice
class PollForm(forms.ModelForm):
class Meta:
model = Poll
class ChoiceForm(forms.ModelForm):
class Meta:
model = Choice
exclude = ('poll',)
So what I want to do is to have several form instances of the Poll and Choice model in a single page, but mind that these models can be repeated too:
<form action="{{url}}" method="post">
{{pollform}}
{{choiceform}}
{{pollform}}
</form>
As you can see there are two Poll forms and one Choice form, but the Poll forms are separated by the Choice form. I do need that the forms keep their order in the page, so is a little harder to use formsets.
The problem I got, is that the values that comes in the post are all by the name "answer", so I get a list of all the elements from all forms by the name "answer" and I can't identify which ones belong to each form.
Don't know if this explanation get a clear view of my problem. Any ideas to get this stuff done?
Thanks for your help!
PD: Don't pay attention to the relation between Poll and Choice, those models are just to clarify the problen, so the relation doesn't matter at all.
Use the prefix kwarg
You can declare your form as:
form = MyFormClass(prefix='some_prefix')
and then, as long as the prefix is the same, process data as:
form = MyFormClass(request.POST, prefix='some_prefix')
Django will handle the rest.
This way you can have as many forms of the same type as you want on the page

Categories

Resources