Form, Create and Update View for Many-To-Many Relationship - python

This is my first question here and I am writing because I'm going mad with this, even after reading documentation and a lot of answers here. Thank you very much and sorry for my bad english!
I have these models:
class Profile(models.Model):
name = models.CharField(max_length = 255, blank = False)
user = models.ForeignKey(User, blank = True, null = True)
class Category(models.Model):
name = models.CharField(max_length = 50, blank = False)
class ProfileCategory(models.Model):
profile = models.ForeignKey(Profile)
category = models.ForeignKey(Category)
class Meta:
unique_together = ('profile', 'category')
Is this model correct?
I suppose to have already the database with the categories saved. I need a page where the user can create a new profile and choose the categories from a list of checkbox. Should I use two form in the same page, one for the profile and one for choose the categories or a single form? I think i need a ModelMultipleChoiceField for the categories.
I also need a view that display the same form already filled with the profile and the categories where the user can change the profile name, and add or remove categories.
Tell me if you need more information and thank you very much.

ProfileCategory model is unnecessary here. Use ManyToMany field to achieve the same result:
class Category(models.Model):
name = models.CharField(max_length=50, blank=False)
class Profile(models.Model):
name = models.CharField(max_length=255, blank=False)
user = models.ForeignKey(User, blank=True, null=True)
categories = models.ManyToManyField(Category, blank=True)
Now you can edit profile with a single form as any other model. The only thing you should to remember is to call save_m2m() in case if you override the form's save() method.
app/forms.py
from django import forms
from app.models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
widgets = {
'categories': forms.CheckboxSelectMultiple,
}
app/views.py
from django.views.generic.edit import CreateView
from app.forms import ProfileForm
from app.models import Profile
class ProfileCreate(CreateView):
form_class = ProfileForm
model = Profile
templates/app/profile_form.html
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Create" />
</form>
app/urls.py
from app.views import ProfileCreate
urlpatterns = patterns('',
...
url(r'^profile/create/', ProfileCreate.as_view()),
)
To update profiles use UpdateView with the same ProfileForm class and template.
EDIT: If you need additional fields in the ProfileCategory model then you can set it as intermediary model with through argument of the ManyToManyField. To edit such models you have to use formsets. Read more about it here, here and here.

Related

Django Form and some ForeingKey fields

Please tell me, when a model has a lot of related fields with other tables, how to make a normal form with filling such a model?
How do I create a form for Project?
class City(models.Model):
obl = models.CharField(max_length=255, choices=REGIONS, default="24", verbose_name="Регион")
name = models.CharField(max_length=128, verbose_name="Город")
population = models.IntegerField()
class Address(models.Model):
city = models.ForeignKey(City, on_delete=models.PROTECT, verbose_name="Город")
street = models.CharField(max_length=255, verbose_name="Улица")
numb = models.CharField(max_length=64, verbose_name="Номер дома")
class Project(models.Model):
manager = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name="Сотрудник")
address = models.ForeignKey(Address, on_delete=models.PROTECT, verbose_name="Адрес")
vis = models.DateField(verbose_name="Подписан дата", blank=True)
accept = models.DateField(verbose_name="Принят дата", blank=True)
Maybe I need a step-by-step fill-in form
You can create and customize the admin form based on your needs. I.e. if you create an admin form for the Project and you would like to include the User form as an Inline form you can achieve that easily by inlines.
Please refer to the documentation regarding adding related objects inside forms.
https://docs.djangoproject.com/en/4.1/intro/tutorial07/
I would follow Jamal's answer if you only need the interface in the admin console.
If you want to create a custom interface in your own, I would do it like this (untested):
# forms.py
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['manager', 'vis', 'accept']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# When we're creating new instance
if not self.instance.pk:
self.address_form = AddressForm(
data=kwargs.get('data'),
files=kwargs.get('files'),
)
# When we're updating an existing instance
else:
self.address_form = AddressForm(
instance=self.instance.address,
data=kwargs.get('data'),
files=kwargs.get('files'),
)
class AddressForm(forms.ModelForm):
class Meta:
model = Address
fields = ['street', 'numb']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# When we're creating new instance
if not self.instance.pk:
self.city_form = CityForm(
data=kwargs.get('data'),
files=kwargs.get('files'),
)
# When we're updating an existing instance
else:
self.city_form = CityForm(
instance=self.instance.city,
data=kwargs.get('data'),
files=kwargs.get('files'),
)
class CityForm(forms.ModelForm):
class Meta:
model = City
fields = '__all__'
# views.py
class CreateProject(CreateView):
model = Project
form_class = ProjectForm
template_name = 'project_form.html'
class UpdateProject(UpdateView):
model = Project
form_class = ProjectForm
template_name = 'project_form.html'
# project_form.html
<form method="post">
{% csrf_token %}
{# The project form #}
{{ form.as_p }}
<h2>Address</h2>
{# The address form in the project form. Instead of the address field. #}
{{ form.address_form.as_p }}
<h2>City</h2>
{# The city form in the address form. Instead of the city field. #}
{{ form.address_form.city_form.as_p }}
<input type="submit" value="Save" />
</form>
PS., if by any chance you're using django-crispy-forms package, make sure you don't render the form tags in the AddressForm and CityForm.
PSPS., if you come into a situation where two of the forms you're rendering on the same page have attributes with the same name, you will need to use prefix in your initializer of the forms to namespace these attributes. However, this is not the case in your question, so I didn't want to complicate.

Create an instance from TextInput in CreateView

I have a model.py file that has classes Author and Article. Article has a foreign key referencing Author. I have created a view,blogCreate, using a form ,ArticleForm, in my forms.py file. Since author in class Article is a foreign key, it means that author will be chosen from the Author queryset. This means that the select tag will automatically used by the form, instead I want to use the <input type="text" > tag so that I can create an instance of Author using the input and not select from the queryset.
forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('title', 'content', 'aurthor')
widgets = {
'title': forms.TextInput(attrs={
'class': 'title'}),
'content': forms.Textarea(attrs={
'class': 'text_input',
'name': 'article_content'}),
# Changed to TextInput so it can use <input type="text" >
'aurthor': forms.TextInput(attrs={
'class': 'text_input',
'name': 'aurthor_name'})
}
models.py
from django.db import models
from ckeditor.fields import RichTextField
class Aurthor(models.Model):
name = models.CharField("Author Name", max_length=100)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField("Title", max_length=100)
content = RichTextField(blank=True, null=True)
pub_date = models.DateTimeField("Publish Date", auto_now_add = True)
aurthor = models.ForeignKey(Aurthor, on_delete=models.CASCADE)
def __str__(self):
return self.title
views.py
from .models import Article, Aurthor
from django.views.generic import CreateView
from .forms import ArticleForm
class blogCreate(CreateView):
model = Article
form_class = ArticleForm
template_name = 'BlogHandler/blog.html'
blog.html
<form action="" method="post">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Post</button>
</form>
I finally found a way to do exactly what I wanted, I don't if how efficient it is but it works. Let me know if there is a better way.
forms.py
from django import forms
from .models import Article, Author
class ArticleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(*args, **kwargs)
self.fields['author'] = forms.CharField(max_length=100, required=True)# author is required
class Meta:
model = Article
fields = ('title', 'content', )
In the question, I stated that I'd changed the widget for author to TextInput so that I could enter text not an instance of Author.This didn't work out the way I wanted, so instead I removed author from fields and made a custom field author that is not a field in my model. This way I still get the text input to create my Author instance.
models.py
class Article(models.Model):
title = models.CharField("Title", max_length=100, null=False)
content = RichTextField(blank=True, null=False)
pub_date = models.DateTimeField("Publish Date", auto_now_add = True)
author = models.ForeignKey(Author, on_delete=models.CASCADE, null=True)
I made author nullable in my models.py file but it is okay because I made the custom auhtor field in forms.py required so that all Articles made using the site have an Author. I was getting some error before this change
views.py
class articleCreate(CreateView):
model = Article
form_class = ArticleForm
def form_valid(self, form):
rt = super().form_valid(form)
article = form.save(commit=False)
author_name = self.request.POST['author'].title()
author, created = Author.objects.get_or_create(name=author_name)
article.author = author
article.save()
return rt
Here I first pause the save so that I can create an Author using the text input from the custom field author, which is simple text, if the Author instance already exists it gets else it creates it. Then I save and I'm done.

Django: How to make a form with foreignkey

Hi I am working with Django and I am trying to make a little system to register people and teams.
So far I can create teams and people in the admin site.
Now, I want to make a public form, where i.e. a trainer can register his team.
The Team has a foreignkey to Bundesland (the state).
I want a dropdown list that shows the states, I already made in the admin site. And then chose from it in the form. My "python crash course"-book doesn't cover this, so please help me. The answers I found so far in the documentation and on stackoverflow didn't work for me.
models.py:
from django.db import models
class Bundesland(models.Model):
bdl_kurz = models.CharField(max_length=2) #abbreviation
bdl_lang = models.CharField(max_length=25) #full name
--snip--
class Team(models.Model):
bdl = models.ForeignKey(Bundesland)
name = models.CharField(max_length=40)
plz = models.CharField(max_length=5)
ort = models.CharField(max_length=40)
strasse = models.CharField(max_length=40)
strnr = models.CharField(max_length=5)
telefon = models.CharField(max_length=20)
email = models.EmailField()
--snip--
forms.py:
from django import forms
from .models import Team
class TeamForm(forms.ModelForm):
class Meta:
model = Team
bdl = forms.ModelChoiceField(queryset='bdl_lang.objects.all()), empty_label=None)
fields = ['name', 'plz', 'ort', 'strasse', 'strnr', 'telefon', 'email']
labels = {'plz': 'PLZ', 'ort': 'Ort', 'strasse': 'Straße', 'strnr': 'Hausnr.', 'telefon': 'Telefon', 'email': 'Email'}
new_team.html:
<p>New Team</p>
<form action="{% url 'teilnehmer:new_team' %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Submit data</button>
</form>
views.py: (if important)
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from .models import Bundesland, Gewichtsklasse, Team, Kaempfer
from .forms import TeamForm
--snip--
def new_team(request):
"""Add a new team."""
if request.method != 'POST':
# No data submitted; create a blank form
form = TeamForm()
else:
# POST data submitted; process data.
form = TeamForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('teilnehmer:index'))
context = {'form': form}
return render(request, 'teilnehmer/new_team.html', context)
So I can pick out a number of problems in your code.
bdl = forms.ModelChoiceField(queryset='bdl_lang.objects.all()), empty_label=None) - you do not need the starting quote right after the equal sign.
bdl_lang is a CharField so bdl_lang.objects.all() doesn't make sense. The queryset is an order dict of Django objects (defined via a model class). Replace this with bdl.objects.all(). From here, insert a __unicode__(self) method in your class Bundesland. This method should return the name you want in your choices. From looking at your code, it seems like you would want to return bdl_lang.
You need to include bdl in your fields option.
I hope this helps!

How to display the valueof a newly created custom model field in Django?

I have created a custom model in models.py
class UserProfile(models.Model):
#required by the auth model
user = models.OneToOneField(User, on_delete=models.CASCADE)
NativeLanguage = models.CharField(max_length=100)
LearningLanguage = models.CharField(max_length=100)
And also created an inline in admin.py, like this:
class ProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'UserProfile'
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = (ProfileInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I can give these two new fields values in the admin app, but when I try to call them in my html, I dont know how to reference them.
<p align="center">Native Language: {{????}}</p>
It must be something simple, but I cant seem to get it.
First of all, model field names should be lowercase underscore separated:
class UserProfile(models.Model):
...
user = models.OneToOneField(User, related_name='profile')
native_language = models.CharField(max_length=100)
Later anywhere in the template or a view:
{{ user.profile.native_language }}
First of all html page renders from view.py
you have to send value of Native language from view to html page
in views native language is retrieved from models ..
In views.py ::
from .models import (model class name)
data = model class name.objects.all().values('NativeLanguage')
return render(request, 'html page', {"data":data})
Something like this will render data from database to html page
then in html page
<p align="center">Native Language: {{data}}</p>

Django form with list of foreign key fields

I have the below models for which I'm trying to create a form for:
class Letter(models.Model):
title = models.CharField(max_length=100)
publish_date = models.TimeField()
class LetterRecipients(models.Model):
letter = models.ForeignKey(Letter)
recipient_name = models.CharField(max_length=200)
recipient_rating = models.IntegerField()
has_responded = models.BooleanField()
I'd like a single form that allows the user to enter a title and publish_date for the letter, and in the same form enter multiple recipients by name and rating.
Can anyone help with creating the form model for this? I can't figure out how to have django generate this form using {{ form.as_p }}. I think I'll have to use jQuery to create the additional recipient rows on the HTML page, but how would I get django to parse those into the model?
Any help would be greatly appreciated.
Thanks.
Ark
Ark, you can use ModelMultipleChoiceField in Django form. Here are some roughly example. I create "posts" apps just for quick testing :
forms.py
from django import forms
from django_test.posts.models import Letter, LetterRecipients
class LetterForm(forms.Form):
title = forms.CharField()
publish_date = forms.TimeField()
recepient = forms.ModelMultipleChoiceField(
queryset=LetterRecipients.objects.all()
)
models.py
from django.db import models
class Letter(models.Model):
title = models.CharField(max_length=100)
publish_date = models.TimeField()
class LetterRecipients(models.Model):
letter = models.ForeignKey(Letter)
recipient_name = models.CharField(max_length=200)
recipient_rating = models.IntegerField()
has_responded = models.BooleanField()
def __unicode__(self):
return self.recipient_name
views.py
# Create your views here.
from django_test.posts.forms import LetterForm
from django.shortcuts import render
def index(request):
form = LetterForm()
data = {'form': form}
return render(request, 'posts/index.html', data)
index.html
{% load url from future %}
{{ form.as_p }}

Categories