recently I encountered a rather weird problem with my Django forms. I am currently working on my todo list project. Everything works fine, except that whenever I add a new task, its title seems to be saved in database with additional square brackets and quotes around it. It looks like one-element list, but it is a string. I kinda solved it by displaying the silced version of the string from the database, but it still shows for example while editing a task. Hope anyone has some idea what might be going on?
models.py:
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=35)
completed = models.BooleanField(default=False)
created_date = models.DateTimeField(auto_now_add=True)
# added_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
author = models.CharField(max_length=50, default='')
def __str__(self):
return self.title
forms.py:
from django import forms
from .models import *
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = '__all__'
widgets = {
'title': forms.TextInput(attrs={'class': 'new_task_text', 'placeholder': 'Add new task'}),
}
class TaskEditForm(forms.ModelForm):
class Meta:
model = Task
fields = ['title', 'completed']
widgets = {
'title': forms.TextInput(attrs={'class': 'new_task_text'}),
}
views.py:
from django.shortcuts import render, redirect
from .forms import *
from django.contrib.auth.decorators import login_required
#login_required
def list_homepage(request):
tasks = Task.objects.filter(author=request.user.username)
for task in tasks:
if "['" and "']" in task.title:
task.title = task.title[2:-2] # CharField returns a string that is looking like one-element list. Have no idea why yet.
# try using form instead of modelform.
form = TaskForm()
if request.method == 'POST':
form = TaskForm({**request.POST, **{'author': request.user.username}})
if form.is_valid():
form.save()
else:
print(form.errors)
return redirect('/list/home')
context = {
'page_title': 'Todo list',
'tasks': tasks,
'form': form,
}
return render(request, 'tasks/list.html', context)
#login_required
def update_task(request, pk):
task = Task.objects.get(id=pk)
form = TaskEditForm(instance=task)
if request.method == 'POST':
form = TaskEditForm(request.POST, instance=task)
if form.is_valid():
form.save()
return redirect('../..')
context = {
'form': form,
'page_title': 'Update task',
}
return render(request, 'tasks/update_task.html', context)
#login_required
def delete_task(request, pk):
task = Task.objects.get(id=pk)
if request.method == 'POST':
task.delete()
return redirect('/list')
context = {
'page_title': 'Delete task',
'task': task,
}
return render(request, 'tasks/delete_task.html', context)
list.html:
{% extends 'tasks/base.html' %}
{% block content %}
<div class="new-task-column">
<form method="POST", action="#"> {% csrf_token %}
{{ form.title }}
<input class="submit" type="submit", value="Create task">
</form>
</div>
<div class="task-column">
{% for task in tasks %}
<div class="item-row">
<div class="task">
{% if task.completed == True %}
<s>{{ task }} </s>
{% else %}
{{ task }}
{% endif %}
</div>
<div class="del_upd_container">
<div class="single-upd-container"><a class="edit_button" href="/list/update_task/{{ task.id }}">Update</a></div>
<div class="single-del-container"><a class="edit_button" href="/list/delete_task/{{ task.id }}">Delete</a></div>
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
update_task.html:
{% extends 'tasks/base.html' %}
{% block content %}
<div class="new-task-column">
<form method="POST"> {% csrf_token %}
<span class="update-task-layout">
{{ form }}
<input class="submit" type="submit" name="Update item">
</span>
</form>
</div>
{% endblock content %}
Thank you so much for any help, I really appreciate it!
Had the same problem, solved it by using the method dict() to transform request.POST's weird type (QueryDict) into a normal dict. The rest of the code, like the save() method for example, might need to be a bit adjusted.
Related
I am learning Django and am trying to set up a simple form HTML with form fields which I can send via POST method.
However, if I load HTML page the fields are not visible
The HTML page is create.html
{% extends 'main/base.html' %}
{% block title %}
Create a task
{% endblock %}
{% block content %}
<h1>Create a task</h1>
<form method="post">
{% csrf_token %}
{{ form.title }}
{{ form.task }}
<button type="submit" class="btn btn-success">Add</button>
<span>{{ error }}</span>
</form>
{% endblock %}
<input type="text" placeholder="Enter name" class="form-control"><br>
<textarea placeholder="Enter description" class="form-control"></textarea><br>
My forms.py file is as follows:
from .models import Task
from django.forms import ModelForm, TextInput, Textarea
class TaskForm(ModelForm):
class Meta:
model = Task
fields = ["title", "task"]
widgets = {
"title": TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter name'
}),
"task": Textarea(attrs={
'class': 'form-control',
'placeholder': 'Enter description'
}),
}
and views.py is as this:
from django.shortcuts import render, redirect
from .models import Task
from .forms import TaskForm
def index(request):
tasks = Task.objects.order_by('id')
return render(request, 'main/index.html', {'title': 'Main page of this website', 'tasks': tasks})
def about(request):
return render(request, "main/about.html")
def create(request):
error = ''
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
redirect('home')
else:
error = 'The form has incorrect values'
form = TaskForm()
context = {
'form': form,
'error': error
}
return render(request, "main/create.html")
It appears to me that
{{ form.title }}
and
{{ form.task }}
are not working properly. However, I do not know how to adjust the code to make them visible. I browsed similar questions and suspect it's because GET request is not defined in view, but I do not know how to fix it.
Thank you for any ideas.
#John Gordon helped in comments, I was not passing the context into the render call
return render(request, "main/create.html", context)
fixed my issue
I am building an todo app with Register, Login, Logout functionalities, I created a TaskForm to let user create their tasks, but when is click add it gives an
IntegrityError at / NOT NULL constraint failed: tasks_task.user_id
I tried many different approaches but it gives the same error
it works when i add user in fields in TaskForm but it creates a Dropdown list of All users
tasks/models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Task(models.Model):
name = models.CharField(max_length=200)
completed = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="tasks")
def __str__(self):
return f"{self.name}:{self.user}"
tasks/forms.py
from django import forms
from .models import Task
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = [
'name',
]
tasks/views.py
from django.shortcuts import render, redirect
from .forms import TaskForm
from .models import Task
from django.contrib.auth.decorators import login_required
# Create your views here.
#login_required
def home(request):
tasks = Task.objects.all()
if request.method == "POST":
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('home')
form = TaskForm()
context = {
'tasks': tasks,
'form': form,
}
return render(request, "tasks/home.html", context )
tasks/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name= 'home' )
]
home.html
{% extends 'users/layout.html' %}
{% load crispy_forms_tags %}
{% block body %}
<div class="container pt-5 ">
{% if user.is_authenticated %}
<p style="text: 'center'">User: {{ user.get_username }}</p>
<div class="container">
<form method="post">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4" >Create Task</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</form>
</div>
{% else %}
Login Here
{% endif %}
<div class = "container">
<ul>
{% for task in tasks %}
{% if task in user.tasks.all %}
<li>{{ task.name }}</li>
{% endif %}
{% empty %}
<li>No Tasks for Today</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
forms.py
from django import forms
from .models import Task
class TaskForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
task = super(TaskForm, self).__init__(*args, **kwargs)
self.fields['user'].required = False
class Meta:
model = Task
fields = [
'name',
'user'
]
views.py
# You can save the user with form object then
#login_required
def home(request):
tasks = Task.objects.all()
if request.method == "POST":
form = TaskForm(request.POST)
if form.is_valid():
form_obj = form.save(commit=False)
form_obj.user = request.user
form.save()
return redirect('home')
form = TaskForm()
context = {
'tasks': tasks,
'form': form,
}
return render(request, "tasks/home.html", context )
I am creating a to-do app in django and while updating a task, i want to auto-fill the fields with previous data. Where am i messing up?
This is my views.py for the same:-
task = get_object_or_404(ToDoList, id=id)
if request.method == "POST":
form = UpdateTaskForm(request.POST)
if form.is_valid():
task.description = form.cleaned_data['description']
task.save()
form.save()
return redirect(reverse('list'))
else:
form = UpdateTaskForm(instance=task)
context = {
'form':form,
'task':task,
}
return render(request, 'TaskList/update.html', context)
and this is my forms.py:-
class Meta:
model = ToDoList
fields = ['title', 'description', 'due_date', 'completed']
here is my template file:-
{% load crispy_forms_tags %}
{% block title %} Updating task {% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success" value="Submit"> Save </button>
</form>
{% endblock %}
and here is my models.py:-
from django.utils import timezone
# Create your models here.
class ToDoList(models.Model):
title = models.CharField(max_length=120)
description = models.TextField(help_text='Explain your task!', blank=True)
created_date = models.DateTimeField(default=timezone.now())
due_date = models.DateTimeField(default=timezone.now())
completed = models.BooleanField(default=False)
#Author foreign key
def __str__(self):
return self.title
If you are updating an instance you must also pass the same instance in the POST method also, so after
if request.method == "POST": add form = UpdateTaskForm(request.POST,instance=task)
I guess you are using crispy forms, so according to this question here They are rendering the forms using {% crispy form %} instead of the way you have done as {{ form|crispy }} maybe that is the issue.
I need form where user can create article with several images. I use django-multiupload app for image field. I can select several images but when I try to submit the form I have message under the image field: "Field is empty and field is required". Where is my mistake? Why I have such message when image field is not empty?
Also maybe someone can advice good examples or apps to save several images. I would be very grateful for any help.
models.py:
class Article(models.Model):
description = models.TextField(_('Description'))
class Image(models.Model):
article= models.ForeignKey(Article, on_delete=models.CASCADE)
image = models.FileField(_('Image'), upload_to='images/%Y/%m/%d/')
forms.py:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('description', )
image = MultiFileField()
def save(self, commit=True):
instance = super(ArticleForm, self).save(commit)
for each in self.cleaned_data['image']:
Image.objects.create(image=each, article=instance)
return instance
views.py:
def article_add(request):
data = dict()
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES)
if article_form.is_valid():
article = article_form.save(commit=False)
******
article.save()
data['form_is_valid'] = True
articles = Article.objects.all
context = {'articles': articles}
context.update(csrf(request))
data['html_article'] = render_to_string('project/article_list.html', context)
else:
data['form_is_valid'] = False
else:
article_form = ArticleForm()
context = {'article_form': article_form}
data['html_article_form'] = render_to_string('project/article_add.html', context, request=request)
return JsonResponse(data)
article_add.html:
{% load widget_tweaks %}
<form method="post" action="{% url 'article_add' %}" class="article-add-form">
{% csrf_token %}
{% for field in article_form %}
<div class="form-group{% if field.errors %} has-danger{% endif %}">
<label class="form-control-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% render_field field class="form-control" %}
{% for error in field.errors %}
<div class="form-control-feedback">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
Try adding "min_num" constraint on the image field,
image = MultiMediaField(min_num=1, media_type='image')
EDIT
def article_add(request):
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES)
if article_form.is_valid():
article = article_form.save(commit=False)
******
article.save()
#assume you have already a view in the name 'article_list'.
return redirect('article_list')
else:
article_form = ArticleForm()
context = dict(article_form=article_form)
return render(request, 'project/article_add.html', context)
The problem maybe because you were trying to render two templates in a single view, also when using django template rendering render is a shortcut function, which is mostly preferred to use, rather than string converting and parsing into json.
Also, 'article_list' must be another view, which shows the list of all the articles, after adding a new article, you should consider redirecting to the list view. Here you were trying to render multiple templates, in a single view. You could make of something like this in your list view,
def article_list(request):
articles = Article.objects.all()
context = dict(articles=articles)
return render(request, 'project/article_list.html', context)
Although, these are my personal opinion regarding the code you just shared. Try this approach...
I have created my first app in Django (1.10.5) / Python 3.4. I have a login page and a register page. Which is working fine.
I can create new user and login with that id. Now after the login I want user to fill a form with some information and click on submit. And the information should get stored in the database.
So I created a model first : Model.py
class UserInformation(models.Model):
firstName = models.CharField(max_length=128)
lastName = models.CharField(max_length=128)
institution = models.CharField(max_length=128)
institutionNumber = models.CharField(max_length=128)
cstaPI = models.CharField(max_length=128)
orchidNumber = models.CharField(max_length=128)
This has created a table in the DB.
forms.py
class UserInformationForm(ModelForm):
class Meta:
model = UserInformation
fields = '__all__'
views.py
def home(request):
form = UserInformationForm()
variables = { 'form': form, 'user': request.user }
return render(request,'home.html',variables)
home.html
{% extends "base.html" %}
{% block title %}Welcome to Django{% endblock %}
{% block head %}Welcome to Django{% endblock %}
{% block content %}
<p> Welcome {{ user.username }} !!! Logout<br /><br /> </p>
<form method="post" action=".">{% csrf_token %}
<table border="0">
{{ form.as_table }}
</table>
<input type="submit" value="Submit" style="position:absolute"/>
</form>
{% endblock %}
But when I click on submit button, It does not insert data into my table.
here is the answer, we need to use the request.POST
def home(request):
if request.method == 'POST':
form = UserInformationForm(request.POST)
form.save()
return HttpResponseRedirect('/home/')
else:
form = UserInformationForm()
variables = { 'form': form, 'user': request.user }
return render(request,'home.html',variables)
the first: you need add urls.py to you app
the second: you need to change your views.py to lool like this
`
info = UserInformation()
lastName = request.POST.get('lastName')
...
info.save()
`