I need to display one form, with multiple fields from 2 different models.
Form will contain only part of fields from models, and layout will be made using the crispy forms.
My models:
class Company(BaseModel):
title = models.CharField(_('Company'), max_length=128)
domain = models.CharField(_('Domain'), max_length=128)
class Account(BaseModel):
company = models.ForeignKey(Company)
user = models.OneToOneField(User)
role = models.CharField(_('Role'), choices=ROLES, default='member', max_length=32)
Fields which I want to show in form:
company title, user first name, user last name, user email
Is it even possible? How can I do this?
The other answers on this page involve tossing away the benefits of model forms and possibly needing to duplicate some of the functionality you get for free.
The real key is to remember that one html form != one django form. You can have multiple forms wrapped in a single html form tag.
So you can just create two model forms and render them both in your template. Django will handle working out which POST parameters belong to each unless some field names clash - in which case give each form a unique prefix when you instantiate it.
Forms:
class CompanyForm(forms.ModelForm):
class Meta:
fields = [...]
model = Company
class AccountForm(forms.ModelForm):
class Meta:
fields = [...]
model = Account
View:
if request.method == 'POST':
company_form = CompanyForm(request.POST)
account_form = AccountForm(request.POST)
if company_form.is_valid() and account_form.is_valid():
company_form.save()
account_form.save()
return HttpResponseRedirect('/success')
else:
context = {
'company_form': company_form,
'account_form': account_form,
}
else:
context = {
'company_form': CompanyForm(),
'account_form': AccountForm(),
}
return TemplateResponse(request, 'your_template.html', context)
Template:
<form action="." method="POST">
{% csrf_token %}
{{ company_form.as_p }}
{{ account_form.as_p }}
<button type="submit">
</form>
In your forms.py
from django import forms
class YourForm(forms.Form):
title = forms.CharField()
first_name = forms.CharField()
last_name = ...
In your views.py
from forms import YourForm
from django import views
from models import Company, Account
class YourFormView(views.FormView)
template_name = 'some_template.html'
form_class = YourForm
success_url = '/thanks/'
def form_valid(self, form):
title = form.cleaned_data['title']
...
# do your processing here using Company and Account
# i.e. company = Company.objects.create(title=title, ...)
# account = Account.objects.get_or_create(
# user=..., company=company ...)
# ... more processing
#
# Call company.save() and account.save() after adding
# your processed details to the relevant instances
# and return a HttpResponseRedirect(self.success_url)
def is_valid(self):
# don't forget to validate your fields if need be here
As usual the docs are pretty helpful.
https://docs.djangoproject.com/en/1.7/topics/forms/
Related
models.py is :
class Todo(models.Model):
user=models.ForeignKey(User,on_delete=models.CASCADE,null=True,blank=True)
title=models.CharField(max_length=200)
desc=models.TextField(null=True,blank=True)
complete=models.BooleanField(default=False)
created=models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ['created']
views.py is:
class TaskCreate(generic.CreateView):
model = Todo
fields = '__all__'
template_name = 'create.html'
success_url = reverse_lazy('home')
create.html is:
<body>
go back
{{ form.as_p }}
<form method="post">
{% csrf_token %}
<input type="submit" value="submit">
</form>
</body>
Whenever I submit data from create.html form it doesn't save it to the database and throws this field is required on 'user' field. How do I resolve this?
You probably want to exclude the user field, since it is determined by the logged in user, so:
from django.conf import settings
class Todo(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, editable=False
)
# …
def __str__(self):
return self.title
class Meta:
ordering = ['created']
Then we inject the logged in user in the instance of the form:
from django.contrib.auth.mixins import LoginRequiredMixin
class TaskCreateView(LoginRequiredMixin, generic.CreateView):
model = Todo
fields = '__all__'
template_name = 'create.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
form.instance.user = request.user
return super().form_valid(form)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
Note: In Django, class-based views (CBV) often have a …View suffix, to avoid a clash with the model names.
Therefore you might consider renaming the view class to TaskCreateView, instead of TaskCreate.
I am new to Django and programming in general. I am trying to generate a list of records from a database but with two fields that can be edited.
In the browser it should show a line with the fields:
clientcode, clientname, Reason, comment
Name and description come from the model and are a reference. The user should only be able to capture reason and comments
I have created a forms.py file and a ModelForm. My issue is how do I pass through an individual object. For this example I've limited my dataset to 10 records
In my view file
def home(request):
if request.method == 'GET':
nca = NcaRe.objects.all()[:10]
form = NcaReForm(instance= <what should go in here> )
return render(request, 'NCAComments/home.html', {'form': form, 'nca': nca})
else:
pass
In my model I have a field called primarykey. I'm not sure how to pass this to the form so that I only bring in that record. I have tried looking at the documentation but have not been able to follow it.
My Model py.
from django.db import models
class NcaRe(models.Model):
primarykey = models.IntegerField(blank=True, null=False, primary_key=True)
clientcode = models.CharField(db_column='ClientCode', max_length=200, blank=True, null=True)
clientname = models.CharField(db_column='ClientName', max_length=510, blank=True, null=True)
reason = models.TextField(blank=True, null=True)
comment = models.TextField(blank=True, null=True)
class Meta:
db_table = 'NCA_RE'
Forms.py
from django.forms import ModelForm
from .models import NcaRe
class NcaReForm(ModelForm):
class Meta:
model = NcaRe
fields = ['reason', 'comment']
In html I am trying to loop through and pass the form
{% for n in nca %}
<p> {{n.clientcode}}</p>
<form>
{% csrf_token %}
{{ form }}
</form>
{% endfor %}
In general, you need to just return empty form if the method of request if GET like as form(). I write below sample code that you can do your calculation in after form validation form.is_valid()
views.py
from django.shortcuts import render
from testPhilip.forms import NcaReForm
from testPhilip.models import NcaRe
def home(request):
if request.method == 'GET':
nca = NcaRe.objects.all()[:10]
form = NcaReForm()
elif request.method == 'POST':
form = NcaReForm(request.POST)
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return render(request, 'testPhilip/home.html', {'form': form, 'nca': nca})
You can retrieve the data after form validation in a cleaned format like this:
comment = form.cleaned_data['comment']
Update:
If you want to populate your form fields with values from database or any default values, you can pass them in the 'GET' section as below:
nca_object=NcaRe.objects.get(pk=nca_id)
form=NcaReForm({
'comment':nca_object.comment,
'reason':nca_object.reason,
})
For more information about writing forms refer to Django forms doc
I made a form and there I had a multiple-choice field called artists which I got from my database and while adding a song a user can select multiple artists and save the song.
The artists are a ManyToManyField in Django models.
models.py
class Artists(models.Model):
""" Fields for storing Artists Data """
artist_name = models.CharField(max_length = 50, blank = False)
dob = models.DateField()
bio = models.TextField(max_length = 150)
def __str__(self):
return self.artist_name
class Songs(models.Model):
""" Fields for storing song data """
song_name = models.CharField(max_length = 30, blank = False)
genre = models.CharField(max_length = 30, blank = False)
artist = models.ManyToManyField(Artists)
release_date = models.DateField()
forms.py
class Song_input(forms.Form):
queryset = Artists.objects.only('artist_name')
OPTIONS = []
for i in queryset:
s = []
s = [i, i]
OPTIONS.append(s)
artist_name = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,choices=OPTIONS)
song_name = forms.CharField()
genre = forms.CharField()
release_date = forms.DateField(widget=DateInput)
Now I want to get all the values selected from the form and save to my database. Here the artist_name may have multiple values.
I have tried using the add() and create() methods but can not figure out how to add all the data where one field (artist_name) having multiple data to my database.
I strongly advise to make use of a ModelForm [Django-doc]. Especially since you make use of ManyToManyFields, which are more cumbersome to save yourself.
# app/forms.py
from django import forms
class SongForm(forms.ModelForm):
class Meta:
model = Songs
fields = '__all__'
widgets = {
'artist': forms.CheckboxSelectMultiple,
'release_date': forms.DateInput
}
There is thus no need to specify the fields yourself, you can change the widgets by adding these to the widgets dictionary [Django-doc] of the Meta subclass.
In your view, you can then both render and sae objects with that form:
# app/views.py
from app.forms import SongForm
def add_song(request):
if request.method == 'POST':
form = SongForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('name-of-some-view')
else:
form = SongForm()
return render(request, 'some-template.html', {'form': form})
The form.save() will save the object in the database.
In the template, you can then render the template:
<form method="post" action="{% url 'name-of-add_song-view' %}">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
I am attempting to create a ModelForm that contains (but is not just) a field where unlimited items can be added. To clarify, the common ToDo list example is almost exactly what I want to do, where users can add an item in an input and it is displayed to the user as added to the ToDo list. In it, the user can add as many items to a list as they want before submitting the form that also contains other fields like a title and description.
I have seen how to do this with custom forms, but I am trying to master ModelForms and keep all the forms on this learning project as ModelForms.
My question is, is this possible with ModelForms? I have thought that perhaps an array can be created, where each added item can be appended to the array, and on the final form submission that array can be stored in the db, but it is above my skill level to actually implement that. Is this the right track, or is there a better way? Thanks for helping me learn!
You don't need to store arrays in databases. Just create a new table for the arrays items. In this case, you have to create a Django model for "ToDo" with a many-to-one relationship with User (or List). For example:
class User(models.Model):
name = models.CharField(max_length=30, primary_key=True, blank=False)
# etc...
class ToDo(models.Model):
title = models.CharField(max_length=50, primary_key=True, blank=False)
description = models.TextField(blank=True, null=True)
user = models.ForeignKey(User, blank=True on_delete=models.CASCADE)
Then create its forms:
class UserForm(forms.ModelForm):
class Meta:
fields = [...]
model = User
class ToDoForm(forms.ModelForm):
class Meta:
fields = [...]
model = ToDo
View:
if request.method == 'POST':
user_form = UserForm(request.POST)
todo_form = ToDoForm(request.POST)
if user_form.is_valid() and todo_form.is_valid():
user = user_form.save()
todo_form.save()
todo_form.cleaned_data['user'] = user
return HttpResponseRedirect('/success')
if request.method == 'GET':
context = {
'user_form': user_form,
'todo_form': todo_form,
}
else:
context = {
'user_form': UserForm(),
'todo_form': ToDoForm(),
}
return TemplateResponse(request, 'your_template.html', context)
Template:
<form action="your_view" method="POST">
{% csrf_token %}
{{ user_form.as_p }}
{{ todo_form.as_p }}
<button type="submit">
</form>
I am trying to extend Djangos authentication system and have a problem when trying to create the modelForm for it. As you can see below I have referenced the authentication backend via the suggested OneToOnefield however when I am creating the ModelForm if I try to reference the fields such as 'username', 'password' etc. it spits an error saying they are unknown fields. The form I am creating is a registration from. What am I doing wrong here? Cheers
Model -
class StudentModel(models.Model):
user = models.OneToOneField(User, unique=True)
birth_date = models.DateField()
contact_number = models.IntegerField()
referral = models.CharField(max_length=100, choices=referral_choices)
ModelForm -
from django import forms
from opus_login.models import StudentModel, EmployerModel
class StudentForm(forms.ModelForm):
class Meta:
model = StudentModel
fields = ['username', 'first_name']
Error -
django.core.exceptions.FieldError: Unknown field(s) (username, first_name) specified for StudentModel
A common solution when you need to extend the User model with another model is use two ModelForms: one for User and another for extending model (Student in your case). In the first you access the needed fields of the User model, in the second those of the Student model.
class UserForm(forms.ModelForm):
password = forms.CharField(label='Password',widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat password',widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'first_name')
def clean_password2(self):
.......
return password2
class StudentForm(forms.ModelForm):
class Meta:
model = StudentModel
fields = ['birthdate', 'contact_number']
Then, in the view, you work with two forms instead of one. For example:
def register(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
student_form = StudentForm(request.POST)
if user_form.is_valid() and student_form.is_valid():
user_form.save()
student_form.save()
And in your template you combine both forms in one:
<form action="." method="post">
{{ user_form.as_p }}
{{ student_form.as_p }}
{% csrf_token %}
<p><input type="submit" value="Register"></p>
</form>
You can't directly access One2One fields like this. You need to first create a object of User and add to One2One relation. You can try like this:
from django import forms
from opus_login.models import StudentModel, EmployerModel
class StudentForm(forms.ModelForm):
username = forms.CharField()
first_name = forms.CharField()
class Meta:
model = StudentModel
fields = ['__all__']
def save(self, **kwargs):
student = super().save(commit=False)
user = User.objects.create(username=self.cleaned_data['username'], first_name=self.cleaned_data['first_name'])
user.set_password(self.cleaned_data['password']) #if there is a password field
student.user = user
student.save(commit=True)
return student