I've looked through every similar question (and tried them), but still couldn't find answer.
I have two models:
class Project(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
name = models.CharField(max_length=120, verbose_name = "Название проекта")
url = models.URLField(max_length=120, unique=True, verbose_name = "Полный адрес сайта")
robots_length = models.CharField(max_length=5, default=0)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
def get_absolute_url(self):
from django.urls import reverse
return reverse('projects:detail', args=[str(self.id)])
class ProjectPage(models.Model):
page_project = models.ForeignKey(Project, on_delete=models.CASCADE)
page_url = models.URLField(verbose_name = "Адрес страницы")
page_title = models.CharField(max_length=300, blank=True, verbose_name = "meta-title",default="")
page_description = models.CharField(max_length=300, blank=True, verbose_name = "meta-description",default="")
page_h1 = models.CharField(max_length=300, blank=True, verbose_name = "Заголовок h1",default="")
def __unicode__(self):
return self.page_url
def __str__(self):
return self.page_url
For each model there is a form:
class ProjectFormUpdate(forms.ModelForm):
class Meta:
model = Project
fields = [
"name",
"url",
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': 'Произвольное название'}),
}
class ProjectPageForm(forms.ModelForm):
class Meta:
model = ProjectPage
fields = [
'page_project',
'page_url',
'page_title',
'page_description',
'page_h1',
]
widgets = {
'page_project': forms.HiddenInput()
}
In views.py I have:
def projects_update(request, proj=None):
instance = get_object_or_404(Project, id=proj)
form = ProjectFormUpdate(request.POST or None, instance=instance)
formset_f = modelformset_factory(ProjectPage, form=ProjectPageForm, extra=3)
formset = formset_f(queryset=ProjectPage.objects.filter(page_project__id=proj), initial =[{'page_project': proj}])
if request.method == 'POST':
formset = formset_f(request.POST)
for formset_form in formset:
if formset_form.is_valid() and formset_form.has_changed():
formset_form.save()
if form.is_valid():
form.save()
context = {
'title': "Редактируем проект - "+instance.name,
'form': form,
'formset': formset,
'instance': instance,
}
return render(request, "projects_update.html", context)
And, finaly, html
<form method="POST" action="" class="create-form">
{{ formset.management_form }}
{% csrf_token %}
<div class="row">
<div class="col-lg-6 offset-lg-3 col-md-10 offset-md-1 col-xs-10 offset-xs-1 form-bg">
<h2>Общие данные</h2>
{{ form|crispy}}
<input type="submit" class="btn btn-success" value="Обновить проект" />
</div>
</div>
{% for formset_form in formset %}
<div class="row form-container">
<div class="col-lg-6 offset-lg-3 col-md-10 offset-md-1 col-xs-10 offset-xs-1 form-bg">
<h3>Страница {{forloop.counter}}</h3>
{{ formset_form|crispy}}
</div>
</div>
{% endfor %}
</form>
What I am trying to achieve is: when user enters a page, he gets a form with project name and project URL already filled in. So, he can correct them.
Below, I want to show a filled in form for every page allready created for this project and several empty forms for creating new.
What happens is all initial data is displayed correctly, but when I fill several empty forms - only first empty form is saved each time.
Here is how it was solved:
Included errors properly.
Saw that second to last form lack required field (hiddenInput)
Made changes in view so it looks like:
formset_f = modelformset_factory(ProjectPage, form=ProjectPageForm, extra=3)
formset = formset_f(queryset=ProjectPage.objects.filter(page_project__id=proj), initial =[{'page_project': proj}, {'page_project': proj}, {'page_project': proj}])
Initial values now match number of extra forms - every form got it's own foreign key.
Probably there is a better solution, but the the problem is found and solved for me!
My problem was that when I tried to render every form of the formset manually I added an unneded <form></form> html element
wrong:
{ form.management_form }}
{% for form in formset %}
<form class="form-class">
{{form.name}}
</form>
right:
{ form.management_form }}
{% for form in formset %}
<div class="form-class">
{{form.name}}
</div>
After that change my forms were recognized correctly.
Related
I'm trying to get a list of objects in Django from a model.
I just want to get the list of 'dht node' from the request user, but it shows nothing in the html file (as if the list was empty). The user that I'm using has 2 'dht nodes' and they're shown in the django admin.
I don't know what is wrong, because if I use the instruction "member.dht.create(...)" in the views function and a create a new 'dht node' like this, this is shown. Only 'dht nodes' that I enter by form do not show. Can be the form?
Thanks a lot, Here's my code:
Models.py
class Node(models.Model):
name = models.CharField(primary_key=True, null=False, max_length= 50)
description= models.CharField(default=None, null=False, max_length= 250)
topic=models.CharField(default=None, null=False, max_length= 50, unique=True)
def __unicode__(self):
return self.name
class dht(Node):
temp = models.IntegerField(default=None, null=True)
hum = models.IntegerField(default=None, null=True)
class UserProfile(User):
uid = models.CharField(default=None, null=False, max_length= 250)
dht = models.ManyToManyField(dht, blank=True)
def __unicode__(self):
return self.user.username
Views.py -dht list-
#login_required(login_url = '/web/login')
def listDhtSensor(request):
member = request.user.userprofile
list = member.dht.all()
return render(request, 'web/listDhtSensor.html', {'list':list})
Html -listDhtSensor.html-
{% block content %}
{% for dht in list %}
{{ dht.name }}
{{ dht.topic }}
{% endfor %}
{% endblock %}
Forms.py
class newDHTSensorForm(forms.ModelForm):
class Meta:
model = dht
field = ['name',
'description',
'topic',]
labels = {'name': 'Name' ,
'description': 'Description',
'topic': 'Topic',}
exclude = ['temp', 'hum']
Views.py -dht form-
#login_required(login_url = '/web/login')
def newDHTSensor(request):
if request.method == "POST":
form = newDHTSensorForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.save()
return redirect('/web/dhtDetail')
else:
form = newDHTSensorForm()
return render(request, 'web/newDhtSensor.html', {'form': form})
Html -newDhtSensor.html-
{% block content %}
<div class="boxReg">
<form method="post">
{% csrf_token %}
<h2>{{ form.name.errors.as_text }}</h2>
<p><label for="id_name">Name:</label> <input class="barraForm" type="text" name="name" maxlength="150" autofocus="" required="" id="id_name"></p>
<p><label for="id_description">Description:</label> <input class="barraForm" type="text" name="description" maxlength="150" id="id_description"></p>
<h2>{{ form.topic.errors.as_text }}</h2>
<p><label for="id_topic">Topic:</label> <input class="barraForm" type="text" name="topic" maxlength="254" id="id_topic"></p>
<div class="boxButtonReg">
<button class="buttonReg" type="submit">Save</button>
</div>
</form>
</div>
{% endblock %}
It shows nothing because you did not link you dht objects to that UserProfile, so if you later query for the dhts for that UserProfile, evidently the list is empty. You should add it to the dht relation, like:
#login_required(login_url = '/web/login')
def newDHTSensor(request):
if request.method == "POST":
form = newDHTSensorForm(request.POST)
if form.is_valid():
post = form.save()
request.user.userprofile.dht.add(post)
return redirect('/web/dhtDetail')
else:
form = newDHTSensorForm()
return render(request, 'web/newDhtSensor.html', {'form': form})
Note that you first need to save the post, so you should omit the commit=False aprameter.
I'm trying to add functionality to add a IntegerField next to every 'stockItem' in the template so that the user can type how many of that item was needed and then update the 'count' value in the 'Stock' model. As for now it only works when the user only selects one item. (I know that how I implement this now would never work but I try to show how I intend it to work)
Models:
class Machine_Service(models.Model):
title = models.CharField(max_length = 100)
stockItem = models.ManyToManyField(Stock)
date = models.DateTimeField(default=timezone.now)
comment = models.TextField()
machine = models.ForeignKey(Machine, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Stock(models.Model):
title = models.CharField(max_length=100)
count = models.IntegerField(default=10)
minLimit = models.IntegerField(default=1)
resellerCompany = models.CharField(max_length=100)
resellerPerson = models.CharField(max_length=100)
resellerEmail = models.EmailField()
def __str__(self):
return self.title
(I left the 'Machine' model out of this because it does not belong to the question)
view:
def CreateService(request):
if request.method == 'POST':
form = CreateServiceForm(request.POST)
if form.is_valid():
m = Machine.objects.get(id=form['machine'].value())
service = Machine_Service(title=form['title'].value(), date=form['date'].value(), comment=form['comment'].value(), machine=m)
service.save()
items = form['stockItem'].value()
for item in items:
s = Stock.objects.get(id=item)
service.stockItem.add(s)
service.save()
return redirect('Machines-Home')
else:
form = CreateServiceForm()
context = {
'form': form
}
form:
class CreateServiceForm(forms.ModelForm):
count = forms.IntegerField(required=False)
class Meta:
model = Machine_Service
fields = ['title', 'stockItem', 'count', 'date', 'comment', 'machine']
template:
(I do not need to use crispy)
{% extends "maskinNytt/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Post!</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
In your case your models.py should look something like this:
class Machine_Service(models.Model):
stockItem = models.ManyToManyField(Stock, through='NeededItems')
...
class Stock(models.Model):
...
class NeededItem(models.Model):
machine_service = models.ForeignKey(Machine_Service, on_delete=models.CASCADE)
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
amount = models.IntegerField()
To specify this amount and use it in your views/forms you need to just create instances of NeededItem.
I assume you have a form for the MachineService and one for a NeededItem. For the NeededItems you need a FormSet, views.py:
from django import formset_factory
NeededItemFormset = formset_factory(NeededItemForm, extra=2)
def view(request):
if request.method == 'POST':
machine_form = MachineForm(request.POST, prefix='machine')
needed_item_formset = NeededItemFormset(request.POST, prefix='items')
if machine_form.is_valid() and needed_item_formset.is_valid():
machine_form.save()
needed_item_formset.save()
return redirect('blabla')
else:
machine_form = MachineForm()
needed_item_formset = NeededItemFormset()
return render('template', {'m_form': machine_form, 'n_form': needed_item_formset})
The template then might look something like this.
<form>
{% machine_service_form.as_p %}
{% needed_item_formset.as_p %}
</form>
(I didn't test this, so I don't give guarantees it works, but I think you should have enough pointers to figure out which things you need).
When one would make a Machine_Service they would also need a form (or multiple) for the NeededItem model. That means you can basically ignore the Stock model when creating Machine_Services.
So I have these two models Lecture and FileUpload. I want that the user to be able to add a lecture to a specific course and also upload multiple files for that lecture. Thing is that after I submit the form the information gets saved except for the uploaded files, so I am guessing something is wrong with my formset. Please have a look:
FileFormset = inlineformset_factory(Lecture, FileUpload, exclude=[])
def classroom(request):
if request.method == 'POST':
form1 = LectureForm(request.POST)
if form1.is_valid():
lecture = form1.save()
formset = FileFormset(request.POST, request.FILES, instance=lecture, prefix='files')
if formset.is_valid():
formset.save()
else:
print(formset.errors)
formset.save()
return redirect('courses:index')
else:
form1 = LectureForm()
formset = FileFormset()
context = {'teacher_data': TeacherData.objects.all(),
'teachers': Teacher.objects.all(),
'courses': Course.objects.all(),
'form1': form1,
'formset': formset,
}
return render(request, 'courses/classroom.html', context)
<form method="post" action="">
{% csrf_token %}
{{ form1.as_p }}
{{ formset.management_form }}
{% for form in formset %}
{{ form }} <br>
{% endfor %}
<br>
<button type="submit">Add Lecture</button>
</form>
class LectureForm(forms.ModelForm):
class Meta:
model = Lecture
fields = ('course', 'lecture_title', 'lecture_category', 'content')
class FileForm(forms.ModelForm):
class Meta:
model = FileUpload
fields = ('files',)
class Lecture(models.Model):
LECTURE_CHOICES = (
('Courses', 'Courses'),
('Seminars', 'Seminars'),
)
course = models.ForeignKey('Course', on_delete=models.CASCADE, default='', related_name='lectures',)
lecture_category = models.CharField(max_length=10, choices=LECTURE_CHOICES, default='Courses',)
lecture_title = models.CharField(max_length=100, blank=True, null=True)
content = models.TextField(blank=True, null=True)
def __str__(self):
return str(self.lecture_title)
class FileUpload(models.Model):
files = models.FileField(upload_to='documents', null=True, blank=True)
lecture = models.ForeignKey('Lecture', related_name='files', on_delete=None, default=None)
def __str__(self):
return str(self.files)
add enctype attribute to you from widget like,
<form method="post" action="" enctype= multipart/form-data>
Try this
I want to create a form with extra fields :
this is the models.py for regular form :
Boutique :
class Boutique(models.Model):
name = models.CharField(max_length=250)
user = models.ForeignKey(User, default=1)
logo = models.FileField(max_length=250)
def __str__(self):
return self.name
form.py :
class BoutiqueForm(forms.ModelForm):
class Meta:
model = Boutique
fields = ['name', 'logo']
Views.py :
def create_boutique(request):
if not request.user.is_authenticated():
return render(request, 'produit/login.html')
else:
form = BoutiqueForm(request.POST or None, request.FILES or None)
if form.is_valid():
boutique = form.save(commit=False)
boutique.user = request.user
boutique.logo = request.FILES['logo']
file_type = boutique.logo.url.split('.')[-1]
file_type = file_type.lower()
if file_type not in IMAGE_FILE_TYPES:
context = {
'boutique': boutique,
'form': form,
'error_message': 'Image file must be PNG, JPG, or JPEG',
}
return render(request, 'produit/create_boutique.html', context)
boutique.save()
return render(request, 'produit/detail.html', {'boutique': boutique})
context = {
"form": form,
}
return render(request, 'produit/create_boutique.html', context)
form_template.html :
{% for field in form %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<span class="text-danger small">{{ field.errors }}</span>
</div>
<label class="control-label col-sm-2">{{ field.label_tag}}</label>
<div class="col-sm-10">{{ field }}</div>
</div>{% endfor %}
i want to add a table boutique_commerçant that containt 2 extra fields facebook_link and entreprise ... that appear in create_boutique.html only one time for new registred user...
Have a look at this question and answer.
You can add an extra field which defaults to False and is set to True if your two fields are saved. This way you can check if the information exists, and only show the form when not.
Im new in Django, and Im trying to create a form for add books for my app. But I want the date of publication not included in the form. Instead I want the current system date is obtained and will " add" the form to save my model . How could I do this?
There is part of my views.py:
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save(commit=False)
new_book.publication_date = django_timezone
new_book.save()
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
There is my forms.py:
class BookForm(ModelForm):
class Meta:
model = Book
exclude = ('publication_date',)
And my model Book:
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
I used this template for form:
{% extends 'base.html' %}
{% block title %}Add a new Book{% endblock %}
{% block content %}
<h3> Here you can add a new book into the local DataBase </h3>
<form action="." method="post">{% csrf_token %}>
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="id_title">Book Title</label>
{{ form.title }}
</div>
<div class="fieldWrapper">
{{ form.authors.errors }}
<label for="id_authors">Authores</label>
{{ form.authors }}
</div>
<div class="fieldWrapper">
{{ form.publisher.errors }}
<label for="id_publisher">Publishers</label>
{{ form.publisher }}
</div>
<div class="fieldWrapper">
{{ form.num_pages.errors }}
<label for="id_num_pages">Number of Pages</label>
{{ form.num_pages }}
</div>
<p><input type="submit" value="Submit"></p>
</form>
{% endblock %}
I've temporarily disabled Django csrf because I do not need for my purpose
To do that, you need to pass the default argument as timezone.now in publication_date model field.
models.py
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
# pass the default argument in publication_date field
publication_date = models.DateField(default=timezone.now)
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
views.py
After doing that, you can directly call .save() on modelform. Now, the book object will be created with the aware datetime.
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save() # directly save the book object
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
Why we did not use auto_now_add argument in the model field?
From django's auto_now_add documentation:
Django's auto_now_add uses current time which is not timezone aware. So, it is better to explicitly specify default value using default argument.
If you want to be able to modify this field, set default=timezone.now
(from django.utils.timezone.now()) instead of auto_now_add=True.