django - csrf protection - markdown2 - python

In some of my blog posts using django and markdown2, I am trying to include a form as follows:
views.py:
def post_detail(request, slug=None):
instance = get_object_or_404(Post, slug=slug)
if not instance.published:
raise Http404
return render(request, "posts/post_detail.html", {'instance': instance})
My template post_detail.html contains a {{ instance.get_markdown }} variable:
{% extends "base.html" %}
{% block body_content %}
<div>
{{ instance.get_markdown }}
</div>
{% endblock body_content %}
base.html:
<html>
<body>
{% block body_content %}
{% endblock body_content %}
</body>
</html>
models.py:
import markdown2
class Post(models.Model):
...
text = models.TextField(verbose_name=_("Text"))
...
def get_markdown(self):
return mark_safe(markdown2.markdown(self.text))
Example for a saved text in Post:
### Example
<form method = "POST">
{% csrf_token %}
First name:<input type="text" name="firstname">
<input type="submit" value="Submit">
</form>
This way, the page simply shows the string "{% csrf_token %}" within the form. How can I render the post with csrf protection?
My temporary solution is:
post_detail.html:
{% extends "base.html" %}
{% block body_content %}
<div>
{{ instance.get_markdown_text }}
</div>
{% if instance.form %}
<div>
<form method = "POST">{% csrf_token %}
{{ instance.get_markdown_form }}
</form>
</div>
{% endif %}
{% endblock body_content %}
models.py:
import markdown2
class Post(models.Model):
...
text = models.TextField(verbose_name=_("Text"))
form = models.TextField(verbose_name=_("Form"), blank=True, null=True)
...
def get_markdown_text(self):
return mark_safe(markdown2.markdown(self.text))
def get_markdown_form(self):
return mark_safe(markdown2.markdown(self.form))
I'm not very happy with this solution, because of the unnecessary field "form" and method get_markdown_form in Post and post_detail.html.

Related

URL with multiple parameters throws a NoReverseMatch error

I aim to redirect the view after the form has been saved. However django keeps throwing a NoReverseMatch error. Is there a step that I've missed or a misconfiguration?
views.py
def add_carpet(request):
context_dict = {}
carpet_form = forms.CarpetForm()
if request.method == "POST":
carpet_form = forms.CarpetForm(request.POST)
if carpet_form.is_valid():
carpet = carpet_form.save()
return redirect(reverse(
"dashboard_add_images",
kwargs={
"model_type": "carpet",
"id": carpet.id
}
))
context_dict["carpet_form"] = carpet_form
return render(
request,
'dashboard/add_carpet.html',
context_dict
)
def add_images(request, model_type, id):
...
add_images.hml
{% extends 'layouts/dashboard_base.html' %}
{% load django_bootstrap5 %}
{% load static %}
{% block title %}Add Images{% endblock title %}
{% block content %}
<div class="row">
<div class="col justify-content-end">
<h1 class="heading half_space text-muted">
Add Images for the {model}
<span class="divider-left"></span>
</h1>
</div>
</div>
{# Load CSS and JavaScript #}
{% bootstrap_css %}
{% bootstrap_javascript %}
{# Display django.contrib.messages as Bootstrap alerts #}
{% bootstrap_messages %}
{# Display a form #}
<form enctype="multipart/form-data" action="{% url 'dashboard_add_images' %}" method="post" class="form">
{% csrf_token %}
{% bootstrap_form form %}
{% bootstrap_button button_type="submit" content="OK" %}
</form>
{% endblock content %}
From above you can see I added the enctype on the form but still no change.
urls.py
urlpatterns = [
path('add_carpet', views.add_carpet, name="dashboard_add_carpet"),
path('add_images/<str:model_type>/<uuid:id>', views.add_images, name="dashboard_add_images"),]
An example redirect url after a form has been submitted is this:
http://127.0.0.1:8000/dashboard/add_images/carpet/97afd259-6ec4-48fc-869f-2a00bbe29c70
Error:
Reverse for 'dashboard_add_images' with no arguments not found. 1 pattern(s) tried: ['dashboard/add_images/(?P<model_type>[^/]+)/(?P<id>[^/]+)$']

The 'header_image' attribute has no file associated with it

When I create a new post with an image, everything is fine, but if I edit it, I want to delete the image using the "clear" button, then this error appears, and if I change, then nothing changes, but there are no errors
here is models.py
`
from django.db import models
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
body = models.TextField()
header_image = models.ImageField(blank=True, null=True, upload_to="images/", default='#') #new
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', args=[str(self.id)])`
here is views.py
`
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Post
class BlogListView(ListView):
model = Post
template_name = 'home.html'
class BlogDetailView(DetailView):
model = Post
template_name = 'post_detail.html'
class BlogCreateView(CreateView):
model = Post
template_name = 'post_new.html'
fields = ['title', 'author', 'body', 'header_image']
class BlogUpdateView(UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body', 'header_image']
class BlogDeleteView(DeleteView):
model = Post
template_name = 'post_delete.html'
success_url = reverse_lazy('home')
#property
def image_url(self):
"""
Return self.photo.url if self.photo is not None,
'url' exist and has a value, else, return None.
"""
if self.image:
return getattr(self.photo, 'url', None)
return None`
post_base.html
`{% load static %}
<html>
<head>
<title>Django blog</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400"
rel="stylesheet">
<link href="{% static 'css/base.css' %}" rel="stylesheet">
</head>
<body>
<div>
<header>
<div class="nav-left">
<h1>Django blog</h1>
<h2>Admin</h2>
</div>
<div class="nav-right">
+ New Blog Post
</div>
</header>
{% if user.is_authenticated %}
<p>Hi {{ user.username }}!</p>
{% else %}
<p>You are not logged in.</p>
Log In<br>
<p>Sign up</p>
{% endif %}
{% block content %}
{% endblock content %}
</div>
</body>
</html>`
post_detail.html
`
{% extends 'base.html' %}
{% block content %}
<div class="post-entry">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
<p>+ Edit Blog Post</p>
<p>+ Delete Blog Post</p>
<img src="{{ post.header_image.url|default_if_none:'#' }}">
{{ post.body|safe }}
{% endblock content %}`
post_new.html
`
{% extends 'base.html' %}
{% block content %}
<h1>New post</h1>
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
{% endblock content %}`
post_edit.html
`
{% extends 'base.html' %}
{% block content %}
<h1>Edit post</h1>
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>
{% endblock content %}`
enctype='multipart/form-data' means that no characters will be encoded. that is why this type is used while uploading files to server. So multipart/form-data is used when a form requires binary data, like the contents of a file, to be uploaded.
You forgot to add enctype='multipart/form-data' in your post_edit.html form and that's the reason your files aren't sent to Django. Following code should work.
post_edit.html
{% extends 'base.html' %}
{% block content %}
<h1>Edit post</h1>
<form action="" method="post" enctype='multipart/form-data'>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>
{% endblock content %}

Django Model Form validation doesn't show

I needed some validation on a Django ModelForm field. So I changed 2 lines in my models.py (just below). The validation is blocking as necessary, but I can't find the proper way to display the ValidationError. Maybe there is a cleaner way to do this in the model form ?
models.py
class Lexicon(models.Model):
[...]
alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$', _('Only alphanumeric characters are allowed'))
filename = models.CharField(_("Filename"), max_length=40, validators=[alphanumeric])
forms.py
class LexiconForm(forms.ModelForm):
class Meta:
model = Lexicon
fields = ['filename', 'language', 'comment', 'alphabet', 'case_sensitive', 'diacritics']
views.py
#login_required
def new_pls_view(request):
if request.method == 'POST':
form = LexiconForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
return redirect('pls_edit')
else:
form = LexiconForm()
return render(request, 'main/new_pls.html', {
'form': form,
})
template.html
<form class="form-horizontal" method="post" action="{% url 'new_pls' %}">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
[...]
{% if form.is_bound %}
{% if form.filename.errors %}
{% for error in form.filename.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% endif %}
{% if form.filename.help_text %}
<small class="form-text text-muted">{{ form.filename.help_text }}</small>
{% endif %}
{% endif %}
{% render_field form.filename type="text" class+="form-control" id="plsFilename" placeholder=form.filename.label %}
Replacing my entire form by {{ form }} as #Alasdair suggested is working, so I guess something is wrong with my template rendering.
I've simply replaced my error printing by this and the error prints!
<form class="form-horizontal" method="post" action="{% url 'new_pls' %}">
{% csrf_token %}
{{ form.non_field_errors }}
[...]
{{ form.filename.errors }}
{% render_field form.filename type="text" class+="form-control" id="plsFilename" placeholder=form.filename.label %}

Django: can't access OneToOneField after rendering TemplateView Form

I am new to Django and don't understand what really is causing this:
I have a Model Company which has an OneToOneField, creator.
# models.py
class Company(models.Model):
class Meta:
verbose_name = 'Company'
verbose_name_plural = 'Companies'
creator = models.OneToOneField(User, related_name="company", on_delete=models.CASCADE, unique=False, null=True)
name = models.CharField(max_length=50)
I have a TemplateView class to handle get and post requests for creating a Company model:
# views.py
class create_company(TemplateView):
def get(self, request):
form = CompanyCreateForm()
title = "Some form"
return render(request, "form.html", {"form": form, "title": title})
def post(self, request):
form = CompanyCreateForm(request.POST)
if form.is_valid():
comp = form.save(commit=False)
comp.creator = request.user
comp.save()
return redirect('index')
The form is showing correctly also storing when I submit, the problem I am facing is with base.html where I show {% user.company %}; the form template extends it like:
{% extends "account/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="post" action="">
{% csrf_token %}
{{form|crispy}}
<button class="btn btn-success" type="submit">Save</button>
</form>
<br>
</div>
<br>
{% endblock %}
and in base.html I access
{% if user.is_authenticated %}
{% user.company %}
{% endif %}
But user.company is not showing even it is set; it shows only when I redirect to index but not when I render the form.
Can someone help me understand what causes this?
{% if request.user.is_authenticated %}
{% request.user.company %}
{% endif %}
you are not sending any context to the base.html, thus only user wont work.
This was the error when I simulated your code.
Error during template rendering
In template /home/user/django/drf_tutorial/snippets/templates/base.html, error at line 2
Invalid block tag on line 2: 'user.company', expected 'elif', 'else' or 'endif'. Did you forget to register or load this tag?
1 {% if user.is_authenticated %}
2 {% user.company %}
3 {% endif %}
4 {% block content %}{% endblock %}
It gives hint that the code to show company should be variable {{ }} instead of tag {% %}. So the base.html template should be as below.
{% if user.is_authenticated %}
{{ user.company }}
{% endif %}
{% block content %}{% endblock %}

Class view returns object has no attribute 'rindex' error

I'm trying to rewrite my function based view to class based view. It raises this error:
.../test/User1
'UserDetailView' object has no attribute 'rindex'
The problem is probably obvious, I'm new in class based views.
So what to do to be able get any profile using url .../test/username?
My new view:
class UserDetailView(DetailView):
model = User
def get_object(self, queryset=None):
return get_object_or_404(self.model, pk=self.kwargs["pk"])
URLS.PY:
url(r'^test/(?P<username>[a-zA-Z0-9]+)/$', views.UserDetailView(),name="user_detail"),
And template:
{% extends "base.html" %}
{% block content %}
{{ userprofile.as_p }}
{% endblock %}
My old view is this:
def get_user_profile(request, username):
user = User.objects.get(username=username)
jobs = user.jobs.all()
table = MyJobsTable(jobs)
context = {
'my_jobs': table,
"user": user
}
return render(request, 'auth/profiles/my-profile.html', context=context)
And HTML:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load render_table from django_tables2 %}
{% block content %}
{% if user.is_authenticated %}
<h3>{% if user.userprofile.is_translator %} Prekladateľský účet: {% else %} Štandardný
účet: {% endif %}{{ user.username }} </h3>
<ul>
<li>Username: {{ user.username }}</li>
<li>First name: {{ user.first_name }}</li>
<li>Last name: {{ user.last_name }}</li>
<li>Email: {{ user.email }}</li>
<li>Telephone: {{ user.userprofile.telephone }}</li>
<li>Languages: {{ user.userprofile.languages.as_p }}</li>
{# TODO: DOPLNIT ATRIBUTY + ked je aj translator#}
</ul>
{% if user.jobs %}
<p>My Jobs</p>
{% render_table my_jobs %}
{% else %}
<p>You have no jobs</p>
{% endif %}
<form class="navbar-form navbar-right" action="/edit-profile" method="get">
<button type="submit" class="btn btn-success">Edit Your Profile</button>
</form>
<form class="navbar-form navbar-right" action="/register-as-translator" method="get">
<button type="submit" class="btn btn-success">Become A Translator</button>
</form>
{% endif %}
{% endblock %}
URLS.PY:
url(r'^profile/(?P<username>[a-zA-Z0-9]+)/$', views.get_user_profile)
The issue is in your urls.py. With a class-based view, you always need to use the as_view classmethod:
url(r'^test/(?P<username>[a-zA-Z0-9]+)/$', views.UserDetailView.as_view(), name="user_detail"),

Categories

Resources