I have a small question to ask the community. I am wondering why my foreign key will not show up when I go to my edit form. When I go to edit the information, all my data is populated except for status (which is a foreignkey that points from the status table to project table), which is left with nothing selected. I use the same forms.py for both adding and editing information.
models.py
from django.db import models
from clients.models import Clients
from django.contrib.auth.models import User
from settings import STATUS_CHOICES
class Project(models.Model):
client = models.ForeignKey(Clients, related_name='projects')
created_by = models.ForeignKey(User, related_name='created_by')
#general information
proj_name = models.CharField(max_length=255, verbose_name='Project Name')
pre_quote = models.CharField(max_length=3,default='10-')
quote = models.IntegerField(max_length=10, verbose_name='Quote #', unique=True)
desc = models.TextField(verbose_name='Description')
starts_on = models.DateField(verbose_name='Start Date')
completed_on = models.DateField(verbose_name='Finished On')
def __unicode__(self):
return u'%s' % (self.proj_name)
def current_status(self):
try:
return self.status.all().order_by('-id')[:1][0]
except:
return None
class Status(models.Model):
project = models.ForeignKey(Project, related_name='status')
value = models.CharField(max_length=20, choices=STATUS_CHOICES, verbose_name='Status')
date_created= models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.value
class Meta:
verbose_name = ('Status')
verbose_name_plural = ("Status")
views.py
#login_required
def addProject(request):
if request.method == 'POST':
form = AddSingleProjectForm(request.POST)
if form.is_valid():
project = form.save(commit=False)
project.created_by = request.user
project.save()
project.status.create(
value = form.cleaned_data.get('status', None)
)
return HttpResponseRedirect('/project/')
else:
form = AddSingleProjectForm()
return render_to_response('project/addProject.html', {
'form': form, 'user':request.user}, context_instance=RequestContext(request))
#login_required
def editProject(request, proj_id):
proj = Project.objects.get(pk=proj_id)
if request.method == 'POST':
form = AddSingleProjectForm(request.POST,instance=proj)
if form.is_valid():
form.save()
return HttpResponseRedirect('/project/')
else:
form = AddSingleProjectForm(instance=proj)
return render_to_response('project/edit_project.html', {
'form': form}, context_instance=RequestContext(request))
forms.py
from django.db import models
from project.models import Project, Status
from django.forms import *
from django import forms
from settings import STATUS_CHOICES
class AddSingleProjectForm(ModelForm):
status = forms.ChoiceField(choices=STATUS_CHOICES)
class Meta:
model = Project
exclude = ('pre_quote', 'created_by')
Snippet from editproject template:
{% block content %}
<FORM METHOD="POST" ACTION="">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Save Project">
</FORM>
{% endblock %}
Any suggestions would be greatly appreciated.
Thank you.
I think this is because project is defined as a ForeignKey on status, and not the other way around. ForeignKey is a One-To-Many relationship. Each project can have multiple statuses connected to it.
That's why on the project instance you'll find: project.status_set and not project.status.
You'll need to add the status to the form manually, by looking for the latest status connected to the project. Or perhaps change the ForeignKey to a OneToOne relationship, depending on your requirements.
Related
I have a view that renders a comment form along with a template:
views.py
def news(request):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = Article.objects.get(pk=2)
print(comment.post)
comment.author = request.user.username
comment.save()
return HttpResponseRedirect('')
else:
form = CommentForm()
return render(request, '../templates/news.html', context={"form": form})
models.py
class Comment(models.Model):
post = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments', blank=True)
author = models.TextField()
text = models.TextField()
def __str__(self):
return self.text
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ('text',)
In views.py, where comment.post is getting assigned to Article objects, I want the pk to be applied dynamically. I tried doing it in the templates, where putting {{ article.pk }} in the templates output the right pk for the Article object but I wasn't sure how I'd go about applying it to my form.
The templates look simple: Article object, below it a comment form.
The problem is simple, I want the news(request) function to dynamically apply the pk of the current Article object in order to make the comment go to the right post.
You can either use the path if it's unique or you can just add a hidden field and set the article pk as value:
<input name="post" type="hidden" value={{ article.pk }} />
And your form:
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ('text', 'post')
and you can access it from validated data in view.
I'm creating a simple blog application. A user is logged in this application while He/She can comment any post on my blog application. But cant impletement that idea.
This is my models.py file
from django.db import models
# Create your models here.
from user.models import CustomUser
from django.conf import settings
from django.db import models
from django.urls import reverse
class BlogPost(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
blog_title=models.CharField(max_length=200)
blog_description=models.TextField()
blog_pub=models.DateTimeField(auto_now_add=True)
blog_update=models.DateTimeField(auto_now=True)
def __str__(self):
return self.blog_title
def get_absolute_url(self):
return reverse('blog:blog_post', kwargs={'pk': self.pk})
class Comment(models.Model):
blogpost=models.ForeignKey(BlogPost, on_delete=models.CASCADE)
comment=models.CharField(max_length=300)
author=models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,blank=True, null=True)
author_name = models.CharField(max_length=50, default='anonymous', verbose_name=("user name"))
comment_pub = models.DateTimeField(auto_now_add=True)
comment_update = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse('blog:home', kwargs={'pk':self.pk})
def __str__(self):
return self.comment
This is views.py file
class BlogPostSingle(DetailView, FormView):
model=BlogPost
template_name='blog/blog_post_single.html'
#fields = ['blog_title']
form_class = CreateCommentForm
success_url = '/blog/'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
this is my forms.py file
class CreateCommentForm(ModelForm):
class Meta:
model=Comment
fields = ('comment', 'blogpost')
and this is my html file and forms section
{% if user.is_authenticated %}
<h5>Hi, {{user.name}} leave your comment now</h5>
<form action="" method="post">
{% csrf_token %} {{form.as_p}}
<input type="submit" value="Submit comment">
</form>
{% else %}
<p>You're not logged in this site, please log in for comment </p>
{% endif %}
My target Idea: Just user logged on my blog application. He can be able to comment any post on my blog application. And my Comment Models contain two forignkey field.
You should pass the user to your view's context, so it will be available in the template:
class BlogPostSingle(DetailView, FormView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
on get_context_data see https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-display/#detailview
on self.request see
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/#dynamic-filtering
I created a Form using one of my models i.e (Post), for my blog website. The form is meant for writers to post articles. In that form there is an Image attribute where the writer can upload an image. However, when i try to upload an image and post it, i get a feedback saying "field required", i think the form is not recognizing the image am trying to upload onto the the database. please help:
this is the form view from views.py:
def formview(request):
form = PostForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return render(request, 'form.html', {'form':form})
this is from forms.py:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
image = forms.FileField
class Meta:
model = Post
fields = ['category', 'title', 'body', 'image', 'author']
this from my models.py:
class Post(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=100)
pub_date = models.DateTimeField(auto_now_add=True)
body = models.TextField()
image = models.FileField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.IntegerField(default=1)
def __str__(self):
return self.title
this is my forms.html template:
<form method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Post</button>
this is my urls.py:
from django.conf.urls import url
from . import views
app_name = 'posts'
urlpatterns = [
url(r'^$', views.homeview, name='homeview'),
url(r'^(?P<pk>[0-9]+)$', views.postview, name='postview'),
url(r'^category/(?P<pk>[a-zA-Z0-9]+)/$', views.categoryview,
name='categoryview'),
url(r'^author/(?P<pk>[a-zA-Z0-9]+)/$', views.authorview, name='authorview'),
url(r'^add_post/$', views.formview, name='formview'),
]
these are the pics might help explain what am trying to say:
Filling the form and selecting the picture
Error message after trying to post
Thank you
def formview(request):
if request.method == 'POST':
form = PostForm(request.POST,request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
else:
form = PostForm()
return render(request, 'form.html', {'form':form})
this form = PostForm(request.POST,request.FILES),you need add FILES to PostForm
I've been using this great post http://kevindias.com/writing/django-class-based-views-multiple-inline-formsets/ to setup my site. I was wondering how to save the user field automatically to an inline formset in views (I used the blockquote for changes to the original). The RecipeForm in (see also below for context)
self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()
saves nicely automatically but not the
ingredient_form.owner= self.request.user
I know Django suggests using BaseInlineFormSet, but most people suggest saving user field in views.py and not forms or models for many different reasons. I would appreciate any suggestions or answers. Here's the full code:
models.py
from django.db import models
class Recipe(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length=255)
description = models.TextField()
class Ingredient(models.Model):
owner = models.ForeignKey(User)
recipe = models.ForeignKey(Recipe)
description = models.CharField(max_length=255)
class Instruction(models.Model):
recipe = models.ForeignKey(Recipe)
number = models.PositiveSmallIntegerField()
description = models.TextField()
forms.py
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from .models import Recipe, Ingredient, Instruction
class RecipeForm(ModelForm):
class Meta:
model = Recipe
IngredientFormSet = inlineformset_factory(Recipe, Ingredient)
InstructionFormSet = inlineformset_factory(Recipe, Instruction)
views.py
from django.http import HttpResponseRedirect
from django.views.generic import CreateView
from .forms import IngredientFormSet, InstructionFormSet, RecipeForm
from .models import Recipe
class RecipeCreateView(CreateView):
template_name = 'recipe_add.html'
model = Recipe
form_class = RecipeForm
success_url = 'success/'
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet()
instruction_form = InstructionFormSet()
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(self.request.POST)
instruction_form = InstructionFormSet(self.request.POST)
if (form.is_valid() and ingredient_form.is_valid() and
instruction_form.is_valid()):
return self.form_valid(form, ingredient_form, instruction_form)
else:
return self.form_invalid(form, ingredient_form, instruction_form)
def form_valid(self, form, ingredient_form, instruction_form):
"""
Called if all forms are valid. Creates a Recipe instance along with
associated Ingredients and Instructions and then redirects to a
success page.
"""
self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()
ingredient_form.instance = self.object
ingredient_form.owner= self.request.user
ingredient_form.save()
instruction_form.instance = self.object
instruction_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, ingredient_form, instruction_form):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
I did some more research and the solution looks somewhat complex following this guide of how to add custom formset saving but modified for BaseInlineFormset as mentioned above. I realized it will be simpler just to make ModelForms for each Model and then linking them in a view, since I only need one child form at a time in the add a new recipe view and can reuse the ModelForm code.
here's the new code that works great! Feel free to contact if you need more info.
forms.py
from django.forms import ModelForm
from .models import Recipe, Ingredient, Instruction
class RecipeForm(ModelForm):
class Meta:
model = Recipe
exclude = ['owner',]
class IngredientForm(ModelForm):
class Meta:
model = Ingredient
exclude = ['owner','recipe',]
class InstructionForm(ModelForm):
class Meta:
model = Instruction
exclude = ['recipe',]
views.py
from .forms import IngredientForm, InstructionForm, RecipeForm
def add_new_value(request):
rform = RecipeForm(request.POST or None)
iform = IngredientForm(request.POST or None)
cform = InstructionForm(request.POST or None)
if rform.is_valid() and iform.is_valid() and cform.is_valid():
rinstance = rform.save(commit=False)
iinstance = iform.save(commit=False)
cinstance = cform.save(commit=False)
user = request.user
rinstance.owner = user
rinstance.save()
iinstance.owner = user
cinstance.owner = user
iinstance.recipe_id = rinstance.id
cinstance.recipe_id = rinstance.id
iinstance.save()
cinstance.save()
return HttpResponseRedirect('/admin/')
context = {
'rform' : rform,
'iform' : iform,
'cform' : cform,
}
return render(request, "add_new_recipe.html", context)
template: add_new_recipe.html
<!DOCTYPE html>
<html>
<head>
<title>Add Recipe</title>
</head>
<body>
<div>
<h1>Add Recipe</h1>
<form action="" method="post">
{% csrf_token %}
<div>
{{ rform.as_p }}
{{ iform.as_p }}
{{ cform.as_p }}
</div>
<input type="submit" value="Add recipe" class="submit" />
</form>
</div>
</body>
</html>
I am busy building a conference website application where is is necessary to be able to upload articles. These articles should also be able to assigned to reviewers to get downloaded and scored. My problem comes with the uploading of the files. I am not sure what I am doing wrong, but I think my form is submitting the incorrect data since it doesn't run through the 'if form.is_valid:' part. I am still a beginner at this. I have watched multiple tutorials.
This is what my model.py file looks like:
from django.db import models
from time import time
def get_upload_file_name(instance, filename):
return "uploaded_files/%s_%s" % (str(time()).replace('.','_'), filename)
class Article(models.Model):
title = models.CharField(max_length=50)
abstract = models.TextField()
pub_date = models.DateTimeField('Date published')
ffile = models.FileField(upload_to=get_upload_file_name)
def __str__(self):
return "%s" % (self.title)
This is my admin.py file:
from django.contrib import admin
from .models import Article
admin.site.register(Article)
This is my forms.py file:
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('title','abstract','pub_date','ffile')
This is my views.py file:
from django.shortcuts import render, render_to_response
from django.http import HttpResponseRedirect
from .forms import ArticleForm
def upload_article(request):
if request.method == 'POST':
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
instance = ArticleForm(file_field=request.FILES[''])
form.save()
instance.save()
return HttpResponseRedirect('/articles/')
else:
form = ArticleForm()
return render_to_response('submit/form.html', {'form' : form})
And then my HTML template:
{% extends 'base.html' %}
{% block content %}
<form method="post" action="../upload/" enctype="multipart/form-data"> {% csrf_token %}
{{form.as_p}}
<input type="submit" value="Submit" />
</form>
{% endblock %}
There is no problems with my urls.. Can someone please help me. Or at least suggest a third party app that might make this easier?
I can post normal forms that is not uploading files, but I just can't seem to get this one.
Ok, so I found the solution to the posting problem.
models.py
from django.db import models
from django.forms import ModelForm
from time import time
# Function to determine where to place uploaded documents
# Taken from youtube tutorial: https://www.youtube.com/watch?v=b43JIn-OGZU&index=15&list=PLxxA5z-8B2xk4szCgFmgonNcCboyNneMD
def get_upload_file_name(instance, filename):
# return a string: folder_name/time-of-upload + seperated by underscore + filename
# Example: media_files/15-10-2015_Submission1
return "uploaded_files/%s_%s" % (str(time()).replace('.','_'), filename)
class Document(models.Model):
file = models.FileField( blank=False, null=True)
title = models.CharField(max_length=200, default=None)
def __str__(self):
return "%s" % self.title
class DocumentForm(ModelForm):
class Meta:
model = Document
fields = ['title', 'file']
forms.py
from django import forms
from .models import Document
class UploadFileForm(forms.ModelForm):
title = forms.CharField(max_length=200)
file = forms.FileField(
label = 'Select a file',
help_text = 'maximum file size: 50mb',
allow_empty_file=False
)
views.py
##login_required
def upload_file(request):
# Handle file upload
if request.POST:
form = DocumentForm(request.POST, request.FILES)
#print(request.POST['title'], ' ', request.POST['file'])
# print(request.FILES['file'])
if form.is_valid():
#newdoc = Document(title = request.FILES['title'])
form.save()
# Redirect to the document list after POST
# return HttpResponseRedirect(reverse('submissions.views.upload_file'))
return HttpResponseRedirect('/success/')
else:
form = DocumentForm() #A empty, unbound form
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('submissions/submission.html', args, context_instance=RequestContext(request))
So one of the problems was that I had to create a FileField in the model as weel as in the form.