I am writing my code based on the principle don't repeat yourself. I keep violating that rule because I am new to Django but this one should be straight forward.
The code below is no problem for ModelAForm:
model.py
class ModelA(models.Model):
id = model.AutoField(primary_key=True)
name1 = models.CharField(max_length=100)
name2 = models.CharField(max_length=100)
...
right = models.BooleanField(default=True)
class ModelB(models.Model):
id = model.AutoField(primary_key=True)
mod = model.ForeignKey(ModelA, on_delete=models.CASCADE)
above30 = models.BooleanField(default=True)
forms.py
class ModelAForm(forms.ModelForm):
class Meta:
model = ModelA
exclude = ['id']
class ModelBForm(forms.ModelForm):
class Meta:
model = ModelB
exclude = ['id']
But this way I don't see the other fields of modelA in the ModelBForm. How can I do this?
Thanks!
Well, it should not. Because its FK relation between ModelB and ModelA. So in ModelB form, the ModelA entries should appear as a choice field. If you want to show fields of ModelA then try like this:
First, remove the FK reference field from ModelBForm:
class ModelBForm(forms.ModelForm):
class Meta:
model = ModelB
exclude = ['id', 'mod']
Now Use both those forms in your view:
def some_view(request):
if request.method == 'GET':
return render(request, 'template.html', {'form_a': ModelAForm(), 'form_b': ModelBForm()})
if request.method == 'POST':
form_a = ModelAForm(request.POST)
form_a = ModelBForm(request.POST)
if form_a.is_valid() and form_a.is_valid():
instance_a = form_a.save() # get model a instance
instance_b = form_b.save(commit=False)
instance_b.mod = instance_a # set model a instance as FK
instance_b.save()
Render the Form Like this:
<form action="." method="post">
{% csrf_token %}
{{ form_a.as_p }}
{{ form_b.as_p }}
<input type="submit" value="Submit">
</form>
Related
I have a django filter with a dependent drop down to filter car manufactures and models. The models use a charfield and pulls the cars from a db entry.
I would like a place holder to say manufacture and model on their respected fields.
I cant find much online about doing this. The only post I can find relates to using the choice field on the model which wont work for me.
filter
class CarFilterForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['model'].queryset = Post.objects.none()
if 'model_manufacture_id' in self.data:
try:
model_manufacture_id = int(self.data.get('model_manufacture_id'))
self.fields['model_id'].queryset = CarModels.objects.filter(model_manufacture_id=model_manufacture_id)
except (ValueError, TypeError):
pass
class carFilter(django_filters.FilterSet):
class Meta:
model = Post
fields = 'manufacture', 'model'
form = CarFilterForm
html
<form method="get" id="AllCarsForm" data-cars-url="{% url 'ajax-allcars' %}">
{% render_field myFilter.form.manufacture class="cars-filter-widget" %}
{% render_field myFilter.form.model class="cars-filter-widget" %}
<button class="btn btn-primary" type="submit">Search</button>
</form>
models
class Manufactures(models.Model):
manufacture_id = models.AutoField(primary_key=True)
manufacture = models.CharField(max_length=55, default="OEM")
class CarModels(models.Model):
model_id = models.AutoField(primary_key=True)
model = models.CharField(max_length=55)
model_manufacture = models.ForeignKey(Manufactures, on_delete=models.CASCADE)
Try to set the empty_label for the fields:
self.fields['your_field'].empty_label = 'My custom empty label'
The simplest method of doing this is to set the model field default to one that corresponds to your fields.
Example:
class Model(models.Model):
field = models.CharField(max_length=25, choices=CHOICES,
default=DEFAULT, blank=True)
You can also do this in forms:
self.fields['field'].choices = [('', 'Placeholder Text')] + list(
self.fields['field'].choices[1:])
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>
Forgive the logic of the table structure below example. It only meant as a simple example of the situation that I am.
So the situation is that I want to make an employee form page, where the department and line manager might or might not exist already. So I replace the drop-down box with the form field for the foreign key, so they can be created if need all in one step for the user. However, with this kind of dependency, I am not doing the right thing in view to make it work.
If you need more detail please do ask.
If you can make the Title more precise please do.
Thank you for your time.
Model
class Employee(models.Model):
name = models.CharField()
age = models.CharField()
line_manager = models.ForeignKey(LineManager)
department = models.ForeignKey(Department)
class LineManager(models.Model):
manager_name = models.CharField()
department = models.ForeignKey(Department)
class Department(models.Model):
department_name = models.CharField()
Form
class EmployeeForm(ModelForm):
class Meta:
model = Employee
fields = ['name',
'age'
#'line_manager' Not needed
#'department' Not needed]
exclude = ('line_manager', 'department')
class LineManagerForm(ModelForm):
class Meta:
model = LineManager
fields = ['manager_name']
exclude = ('department')
# There is a basic form for Department, as well.
View
class AddEmployeeView(View):
forms = {'department': DepartmentForm(self.request.POST or None),
'line_manager': LineManagerForm(self.request.POST or None),
'employee': EmployeeForm(self.request.POST or None)]}
def get(self, request, *args, **kwargs):
form_list = [form for _,form in forms]
return render (request, 'app/temp.html', {'forms': form_list}
def post(self, request, *args, **kwargs):
if all([form.is_valid() for _,form in forms]):
department_data = forms['department'].cleaned_data
department_obj, _ = Department.objects.get_or_create(department_data)
line_manager_instance = forms['line_manager'].instance
line_manager_instance.department = department_obj
line_manager_data = forms['line_manager'].cleaned_data
line_manager_obj, _ = LineManager.objects.get_or_create(line_manager_data)
employee_instance = forms['employee'].save(commit=False)
employee_instance.department = department_obj
employee_instance.line_manager = line_manager_obj
employee_instance.save()
html
<form method="POST">
{% csrf_token %}
{% form in forms %}
{{ form.as_p }}
{% endform %}
<input type='submit'/>
</form>
I am new in Django, and trying to create a simple blog platform out of it. It's where user can create/edit a blog post on the front end.
Here is my models.py
class Blog(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, null=True, blank=True)
......
class Categories(models.Model):
blog = models.ForeignKey(Blog)
name = models.CharField(max_length=200)
slug = models.CharField(max_length=200)
......
And I created a form in the front end by :
#forms.py
class AddBlogForm(forms.ModelForm):
class Meta:
model = Blog
class AddCategoryForm(forms.ModelForm):
class Meta:
model = Categories
#views.py
def AddBlogPost(request):
if request.POST:
lf= AddBlogForm(request.POST, request.FILES,prefix="Lform")
cf = AddCategoryForm(request.POST, prefix="Cform")
if lf.is_valid() and cf.is_valid():
addblog = lf.save(commit=False)
addblog.save()
obj_id = addblog.id
addcategory = cf.save(commit=False)
addcatgory.blog_id = obj_id
addcatgory.save()
return HttpResponseRedirect(reverse('edit', args=(obj_id,)))
else:
blog= AddBlogForm(prefix="Lform")
category = AddCategoryForm(prefix="Cform")
return render_to_response("forms.html", locals(),context_instance=RequestContext(request))
#template
<form action="" enctype="multipart/form-data" method="POST">{% csrf_token %}
{{blog.as_p}}
{{category.as_p}}
<input type="submit">
</form>
This working actually, but I feel it is too redundant create and saving a form with two classes.It's there anyway to simplified this?
You can use django forms to save data from a form to multiple models. For example:
forms.py
class AddBlogCategory(forms.Form):
title= form.CharField(max_length=100)
content= form.CharField(max_length=255)
...............
...............
views.py:
def AddBlogPost(request):
if request.POST:
form= AddBlogCategory(request.POST)
if form.is_valid():
addblog= Blog() #import model class
addblog.title= form.cleaned_data['title']
addblog.content= form.cleaned_data['content']
addblog.author= request.user
.....
addblog.save()
obj_id= addblog.id
addcat=Categories()
addcat.blog= addblog
....
addcat.save()
return HttpResponseRedirect(reverse('edit', args=(obj_id,)))
else:
return render_to_response("forms.html", {form:'form'})
Its an untested code, but you can have an idea how to implement it.
I have two models like this:
class Person(models.Model):
name = models.CharField(max_length=100)
house = models.ForeignKey('House')
class House(models.Model):
address = models.TextField()
Is there some way to create a model form for Person and have it include inline the form to edit the related House object as well? From what I understand of the inline formsets stuff, I would only use that if I have a form editing a House and I want to display forms for all the related Persons. Any ideas?
Just stick the HouseForm into the PersonForm, evaluate it as part of the clean() process, and save it as part of the save() process. Works in a modelformset too.
class HouseForm(forms.modelForm):
""" Edit a house """
class Meta:
model = House
exclude = ()
class PersonForm(forms.ModelForm):
""" Edit a person and her house """
class Meta:
model = Person
exclude = ()
def __init__(self, *args, **kwargs):
super(PersonForm, self).__init__(*args, **kwargs)
self.fields['house'].required = False
data = kwargs.get('data')
# 'prefix' parameter required if in a modelFormset
self.house_form = HouseForm(instance=self.instance and self.instance.house,
prefix=self.prefix, data=data)
def clean(self):
if not self.house_form.is_valid():
raise forms.ValidationError("House not valid")
def save(self, commit=True):
obj = super(PersonForm, self).save(commit=commit)
obj.house = self.house_form.save()
obj.save()
Then in your markup:
<form ...>
{{ person_form }}
{{ person_form.house_form }}
</form>
You have access to the related House object through the Person. As such, I would use the house object as the instance for a Modelform.
HouseForm(ModelForm):
class Meta:
model = House
Say you have a Person object of Paul with a related House.
house_form = HouseForm(instance=Paul.house)
Is this what you were getting at?
I'm not sure whether it is the best way to solve it, but I would do something like this:
Define a ModelForm from each model:
class PersonForm(ModelForm):
class Meta:
model = Person
class HouseForm(ModelForm):
class Meta:
model = House
Define a template like this one, outputting both forms:
<form action="" method="post">
<table>
{{ form1 }}
{{ form2 }}
</table>
<input type="submit">
</form>
A view to create the form to edit the information from both models.
def edit(request):
# You could grab the id from the request that calls the edit form
p = models.Person.objects.get(pk=request.GET.get('id'))
h = models.House.objects.get(pk=p.house.id)
return render_to_response('template.html',
RequestContext(request,
{'form1': PersonForm(instance=p), 'form2': HouseForm(instance=h)}
)
)
And so on.