My view is a function based view (it is a legacy and due to inline formset usage it is easier to keep it that way, rather than CBV). My goal is to be able to render the ModelForm for the model object Article.objects.get(user=user).latest("id") and with the POST method, update the same object that has been rendered. The issue is that the ModelForm "does not remember" the object it corresponded to. What would be the right way to pass that info about the object to the view for use in the POST method?
Below is the simplified version of the views.py and the urls.py
views.py
from django.contrib.auth.models import User
from django.shortcuts import render_to_response
from django.template import RequestContext
from specify.models import Article
from specify.forms import ArticleForm
def index(request):
user = User.objects.get(username=request.user.username)
if request.method == "POST":
a_form = ArticleForm(request.POST, instance= *???* )
a_form.save()
else:
a = Article.objects.get(user=user).latest("id")
a_form = ArticleForm(instance=a)
return render_to_response(
"specify/index.html",
{
"a_form" : a_form,
},
context_instance=RequestContext(request)
)
urls.py
from django.conf.urls import patterns, url
from specify import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index')
)
EDIT1: added models.py and forms.py
models.py
from django.db import models
class Article(models.Model):
pub_date = models.DateField()
headline = models.CharField(max_length=200)
content = models.TextField()
forms.py
from django.forms import ModelForm
from specify.models import Article
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['pub_date', 'headline', 'content']
First you should pass article id to the template, in view.py
def index(request):
user = User.objects.get(username=request.user.username)
if request.method == "POST":
#Explained later
else:
a = Article.objects.get(user=user).latest("id")
a_form = ArticleForm(instance=a)
article_id = a.id
return render_to_response(
"specify/index.html",
{
"a_form" : a_form,
"article_id": article_id,
},
context_instance=RequestContext(request)
)
Second in your template inside of your form html element you should handle your article id
{% if article_id %}
<input type="hidden" value='{{ article_id }}' id='article_id' name='article_id'/>
{% endif %}
Thirdly in update handling you should do following:
if request.method == "POST":
article_id = request.POST.get('article_id') # You are getting passed article id
a = Article.objects.get(pk=article_id) # You are getting instance by id
a_form = ArticleForm(request.POST, instance=a)
a_form.save()
Try above steps and if you have problem leave a comment
Related
It seems like my models.py, forms.py, urls.py and views.py are not recognizing the elements in each other and I don't understand what could be going wrong. I'm just starting the project (CS50W Project4-Network) and I noticed the problem when I tried to render the model form and the textarea field of the form linked to the model wouldn't show on the browser, it only shows when I click submit, as a missing field to be filled, I'm not sure if its because of the same pylint errors I'm getting or something else.
Here is my models:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
pass
class Post(models.Model):
body = models.TextField()
date = models.DateTimeField(auto_now_add = True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="author", default=None)
def __str__(self):
return f"{self.body} by {self.author} at {self.date}"
The forms.py:
from django import forms
from django.forms import ModelForm
from .models import Post
class PostForm(ModelForm):
class Meta:
model = Post
fields = ["body"]
widgets = {
"body": forms.Textarea(attrs={'class': 'form-control col-md-5 col-lg-6'}),
}
The views
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from .forms import PostForm
from .models import User, Post
**I left out logging in routes**
#login_required(login_url='login')
def create_post_page(request):
return render(request, "network/create_post.html")
def create_post(request):
posted = False
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.author = request.user
instance.save()
return HttpResponseRedirect("/create_post?posted=True")
else:
form = PostForm()
if "posted" in request.GET:
posted = True
return render(request, "network/create_post.html", {
"posted": posted,
"form": form
})
The urls:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
path("create_post", views.create_post, name="create_post"),
path("create_post_page", views.create_post_page, name="create_post_page")
]
The template code where the form is not showing, only showing when I click submit as a missing field to be field
{% extends "network/layout.html" %}
{% block body %}
{% if posted %}
{% else %}
<h2>Create a new post</h2>
<form action="{% url 'create_post' %}" method = "POST">
{% csrf_token %}
{{ form.as_p }}
<input class="btn btn-secondary" type="submit" value="Post">
</form>
{% endif%}
{% endblock %}
The problems vscode keep raising:
No name 'Post' in module 'network.models'Pylint(E0611:no-name-in-module)
Module 'network.views' has no 'create_post' memberPylint(E1101:no-member)
Module 'network.views' has no 'create_post_page' memberPylint(E1101:no-member)
Somebody help me please, I feel like its a small problem but its driving me nuts because I can't figure out why.
Make sure you load the plugin "pylint_django" when pylint is called, and that you pass it the correct value for django-settings-module. To do this, create a file pylintrc in your project folder and give it the following contents:
[MAIN]
load-plugins = pylint_django,
django-settings-module = mysite.settings
What I want to do: I want to have a login form that when details are entered they are saved on the admin side.
My problem: the forms are not showing up on my local host page. See image below:
Here is the code from the login form app:
admin.py:
from django.contrib import admin
# Register your models here.
from .models import Contact
admin.site.register(Contact)
from apps.py:
from django.apps import AppConfig
class ContactConfig(AppConfig):
name = 'contact'
from forms.py
from .models import Contact
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
fields = ('username', 'password')
from models.py:
class Contact(models.Model):
username = models.CharField(max_length=100)
password = models.CharField(
max_length=100,
)
def __str__(self):
return f'{self.username} {self.password}'
from views.py:
# Create your views here.
from .forms import ContactForm
def contact(request):
template = "home2.html"
if request.method == "POST":
form = ContactForm(request.POST)
if form.is_valid():
form.save()
else:
form = ContactForm()
context = {
'form': form,
}
return render(request, template, context)
Then finally from the login page:
{% load static %}
<form method="post" class="form">
{% csrf_token %}
{{ form }}
<button type="submit" class="btn">Log In</button>
</form>
Another thing: forms are connected to the admin side but just do not appear on the login page
I would like to link the user who created the post by their username that way people know who it was posted by but I can't seem to get it to work and yes I am logged in and I have a working register and login form already.
Every time I go to submit some news from the form when logged in I get this error NOT NULL constraint failed: news_news.author_id
models.py
from django.db import models
from django.contrib.auth.models import User
from markdownx.models import MarkdownxField
from markdownx.utils import markdownify
from taggit.managers import TaggableManager
class News(models.Model):
author = models.ForeignKey(User, unique=True, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
short_desc = models.CharField(max_length=500)
content = MarkdownxField()
tags = TaggableManager()
slug = models.SlugField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
views.py
from django.shortcuts import render, redirect, get_object_or_404
from .models import Category, News
from .forms import NewNewsForm
from django.shortcuts import render
def show_news_view(request):
news = News.objects.values('author', 'title', 'short_desc', 'tags', 'created_at', 'updated_at')
context = {
'news': news
}
return render(request, "news/news_home.html", context)
def new_news_form_view(request):
if request.method == "POST":
form = NewNewsForm(request.POST or None)
if form.is_valid():
form.save()
form = NewNewsForm()
return redirect('/news')
else:
form = NewNewsForm()
context = {
'form': form
}
return render(request, "news/news_form.html", context)
EDIT:
forms.py
from django import forms
from .models import News
class NewNewsForm(forms.ModelForm):
class Meta:
model = News
fields = ['title', 'short_desc', 'content', 'category', 'tags', 'slug']
news_form.html
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<form action="" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% else %}
<p>Please login before you can submit a news story.</p>
{% endif %}
{% endblock content %}
You are not passing the user.
Later edit with a simpler solution:
Simply pass commit=False when saving the form. Which will create your News object without commiting it to DB. Simply set the author afterwards and save the object.
def new_news_form_view(request):
if request.method == "POST":
form_data = request.POST or None
form = NewNewsForm(form_data)
if form.is_valid():
news = form.save(commit=False)
news.author = request.user
news.save()
return redirect('/news')
else:
form = NewNewsForm()
context = {
'form': form
}
return render(request, "news/news_form.html", context)
I have used django's models and forms to add a comment section to a blog app, however the form's text field will not show when loaded in the in the browser. Only the submit button loads with no textfield to submit with.
models.py
from django.db import models
from datetime import datetime
from django import forms
class Post(models.Model):
title = models.CharField(max_length=140)
body = models.TextField()
date = models.DateTimeField("date published", default=datetime.now())
def _str_(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey('blog.post', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(max_length=100)
text = models.TextField(max_length=200)
created_date = models.DateTimeField(default=datetime.now())
def _str_ (self):
return self.text
forms.py
from django import forms
from django.forms import ModelForm
from .models import Comment
from django.db import models
class CommentForm(forms.ModelForm):
class Meta:
models = Comment
exclude = ['post']
form = CommentForm
post.html
<div>
<div class="container">
<h5>New comment</h5>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<br>
<button class="btn" style="background-color:lightblue" type="submit">Submit Comment</button>
</form>
</div>
</div>
views.py
from django.shortcuts import render
from django.views import generic
from .forms import CommentForm
from .models import Comment
def add_comment_to_post(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('/blog/<int:pk>', pk=post.pk)
else:
form = CommentForm()
return render(request, 'blog/post.html', {"form":form})
form = CommentForm(request.POST)
form = CommentForm()
In django's admin i can add new comments and the form loads as expected there but does not work on the blog post itself. Admin submitted comments through the admin panel are also saving and displaying fine.
you have a typo in forms.py
change models to model
from django import forms
from django.forms import ModelForm
from .models import Comment
from django.db import models
class CommentForm(forms.ModelForm):
class Meta:
model = Comment # changed from models
exclude = ['post']
in views.py
you are missing imports
from django.shortcuts import get_object_or_404,redirect
from .models import Post
other changes
in your models.py
the to string method for classes is __str__ not _str_
with this changes i was able to reproduce this django project with your snippets that works in posting comments
I've only been at python for about 2 weeks now and I've run into an issue which I've been trying to figure out for the past 3 days. I've read through the official documentation and pretty much every tutorial and youtube video available.
I'm trying to build a simple blog as a first project and would like to have a section where people can post comments. I have set up the comments model and a modelform to go along with it. However, I can't figure out how to get django to create the form for me in the template, nothing is displayed.
models.py
from django.db import models
from django.forms import ModelForm
class posts(models.Model):
author = models.CharField(max_length = 30)
title = models.CharField(max_length = 100)
bodytext = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.title
class comment(models.Model):
timestamp = models.DateTimeField(auto_now_add=True)
author = models.CharField(max_length = 30)
body = models.TextField()
post_id = models.ForeignKey('posts')
def __unicode__(self):
return self.body
class commentform(ModelForm):
class Meta:
model = comment
views.py
from django.shortcuts import render_to_response
from blog.models import posts, comment, commentform
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.context_processors import csrf
def home(request):
entries = posts.objects.all()
return render_to_response('index.html', {'posts' : entries})
def get_post(request, post_id):
post = posts.objects.get(id = post_id)
context = {'post': post}
return render_to_response('post.html', context)
def add_comment(request, post_id):
if request.method == 'POST':
form = commentform(request.POST)
if form.is_valid():
new_comment = form.save(commit = false)
new_comment.post_id = post_id
new_comment.save()
else:
form = commentform()
context = RequestContext(request, {
'form': form})
return render_to_response('post.html', context)
Urls.py
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', 'blog.views.home', name='home'),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^(?P<post_id>.*)/$', 'blog.views.get_post'),
url(r'^post_comment/(\d+)/$','blog.view.add_comment'),
post.html
{% extends 'base.html' %}
{% block content %}
<p><h3> {{ post }} </h3></p>
<p> {{ post.bodytext }} </p>
<br><br><br><br>
<form action="/post_comment/{{ post.id }}/" method="POST"> {% csrf_token %}
{{ form }}
<input type="submit" value="Post">
</form>
{% endblock %}
I do get a 403 error CSRF verification failed but, the more pressing issue I think is that the {{ form }} doesn't do anything in the template.
I believe home function and get_post function are working and all the urls are working properly. So, I assume there's something wrong with the add_comment function or in posts.html.
Thanks
Django isn't magic. Your comment form is in a completely different view from the one that displays the blog post. So how is Django supposed to know to render it? You need to actually pass the form object to the template somehow.
The way that this was done in the old contrib.comments app was to have a simple template tag which was responsible for just displaying the comment form for an object, which you could place on any template. The form itself submitted to its own view as usual.
One thing you might check is the syntax of the render_to_response call. I believe you'll want to define it like this. (Maybe your syntax will work too, and this isn't the issue, but I have mostly seen the call made like this). This could be the cause of the missing form in the context.
return render_to_response('post.html',
{'form': form},
context_instance=RequestContext(request))
Let me know if this works. Hope this helps,
Joe
Reference: https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#django.shortcuts.render_to_response