Django How to display a link from user input form safely - html

I have an model (posts) that is tied to a form that when a certain character is used it creates a link to another portion of the web app. The user can enter several of these characters/links to different areas of the webpage in one post then I want to display all of the users posts to the user in a list looping over the model that stores the link. However, how can I do this while maintaining security? I do not want to allow the user to enter in HTML and it be rendered.
For example:
User enters form information:
"Hello this is a #test and #about is a test too"
User selects submit button and background magic to get the words test and about and convert them into links to take the user to the test and about pages in the web application.
Display all of the inputs/posts that have been created:
"Hello this is a #test and #about is a test too"
I know that I can use the safe tag and just store the HTML link in the model and call like normal but since this is from user input I don't want to allow them to create HTML inside of the input form. How can I achieve this desired result safely?
Loosely what I have now:
views.py
def index(request):
if request.method == "POST":
post_form = PostForm(request.POST, request.FILES)
if post_form.is_valid():
# TODO Add background magic here
post_form.save()
messages.success(request, ('Your post was successfully added!'))
else:
messages.error(request, 'Error saving form')
return redirect("/posts")
post_form = PostForm()
posts = Post.objects.all()
return render(request=request, template_name="posts/page.html", context={'post_form':post_form, 'posts':posts})
page.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Testing</title>
</head>
<body>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ post_form }}
<button class="btn btn-primary my-4" type="submit">Submit</button>
</form>
{% for post in posts %}
<p>{{post.id}}</p>
<p>{{post.post}}</p>
{% endfor %}
</body>
</html>

Related

Why is Flask not loading images the second time I render a template?

I have made a ML model which I am deploying in a website using Flask. This is the code for it:
main.py
#imports
app = Flask(__name__)
def check(url):
#function to check whether the URL entered by user is Phishing or not and return the result
#app.route('/')
def home():
return render_template("home.html") #renders homepage template
#app.route('/phish/<url>')
def phish(url):
for i in range(0,len(url)):
if url[i]=="$":
url = url[:i]+"/"+url[i+1:] #changing url back to original
return render_template("phish.html", url=url)
#app.route('/safe/<url>')
def safe(url):
for i in range(0,len(url)):
if url[i]=="$":
url = url[:i]+"/"+url[i+1:] #changing url back to original
return render_template("safe.html", url=url)
#app.route('/', methods=['POST'])
def results():
url = request.form['url']
result = check(url)
for i in range(0,len(url)):
if url[i]=="/":
url = url[:i]+"$"+url[i+1:] #changing "//" to "$$"
if result==1 :
return redirect(url_for("phish", url=url))
else :
return redirect(url_for("safe", url=url))
if __name__ == '__main__':
app.run()
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- bunch of meta tags -->
<title>PhishLearn</title>
<link href="{{ url_for('static', filename='css/bootstrap.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet"> <!-- for loading
css -->
</head>
<body>
<!-- body code -->
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
</body>
</html>
The code for phish.html and safe.html is also exactly same except for some changes in the body part.
Now when I initially run my main.py file the home page is loaded perfectly. But when I input a URL and click submit the page that is then rendered according to the result (phish or safe) does not display any image. Here are the screenshots of the pages:
Homepage
Result Page
As you can see when safe.html is rendered it does not shows any images. Can anyone tell me why this is happening and how to solve this issue?
I just use the src attribute of img tag to specify the path of file. Since all my html resources (css, js, images, etc.) are inside the static folder so I write src = "static/img/image-name.jpg"
That's your problem (as I kind of guessed).
When the URL in your browser is /phish/foople.blarp.quux, then static/img/image-name.jpg relative to that path is /phish/foople.blarp.quux/static/img/image-name.jpg which obviously 404s.
You need to
use the {% url_for("static", filename=...) %} form so Flask will deal with forming the correct static URL,
or alternately but less preferably (since changing the static files' root path will then require you to edit every template you have) use an absolute path /static/img/image-name.jpg.
or if you feel like using esoteric and often-forgot-about HTML features, set <base> and never worry about relative paths, but really please don't do that.

How can I iterate through each item in a database and display them in divs on my html page with django?

I have some code written to find the first post in the database and output it to my page, but I', not sure how to go about iterating through the database. I've done similar things before but this is my first time using django and bootstrap.
My view currently looks like this:
def gallery_view (request):
obj = GalleryPost.objects.get (id =1)
context = {
'object' : obj
}
return render(request, "gallery.html", context)
This works well enough for 1 object but as you can see it takes a set ID of 1, so I need to somehow iterate this to fetch every item from my DB and somehow output them properly.
View:
def gallery_view(request):
qs = GalleryPost.objects.all()
context = {
'objects' : qs
}
return render(request, "gallery.html", context)
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for object in objects %}
<div>
{{object}}
</div>
{% empty %}
<p>No objects found</p>
{% endfor %}
</body>
</html>
Instead of .all() you can also use .filter() to filter the queryset.

image path is not identifiable on index.html of django3

i am following a tutorial based on django 2 and currently running version 3 of django, the problem i am facing is the identifying path of the tag on the front end of index.html, i'll post the code bellow , kindly tell me where i went wrong and any other mistakes
my settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS= (os.path.join(BASE_DIR, 'static'),) #static root added (A2)
MEDIA_ROOT=os.path.join(BASE_DIR, 'media')
MEDIA_URL='/media/'
my urls.py of main project
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('',include('posts.urls')),
path('user/',include('user.urls')),
path('admin/', admin.site.urls),
]+ static(settings.STATIC_URL, document_root=settings.MEDIA_ROOT)
my models.py of app
class Posts(models.Model): #created this model field
def min_len(val): #this is the custom validator working
if len(val)<=9:
raise validators.ValidationError("%(val)s Must be more than 10", params={'val':val})
title=models.CharField(validators=[min_len],max_length=255) #validation list provided
content=models.TextField(validators=[min_len])
thumb_nail=models.FileField(upload_to="posts/",null=True) #file field added
class Posts_Form(forms.ModelForm): #to build a form using django and not html
class Meta: #will get data of the fields=[] only
model=Posts
fields=['title','content','thumb_nail','user','category']
views.py
def index(request):
form=Posts_Form() #created form object
data=Posts.objects.all() #retrive data from the model to display
category=Category.objects.all() #we get all data of category
if request.method=='POST':
form=Posts_Form(request.POST,request.FILES) #request.FILES for file data
if form.is_valid():
# data=Posts(
# title=request.POST['title'],
# content=request.POST['content'],
# )
form.save()
return redirect('/')
return render(request,'index.html',{'title':'Add New Post','form':form,'rows':data,'categories':category})
and my index.html
{% extends "layout.html" %}
{% load static %}
{% block content%}
{%for row in rows%}
<h2>
{{row.title}}
</h2>
<p>
{{row.content}} - <small> {{row.created_at}}-{{row.user.username}} </small>
</p>
<p><img src="{%static 'row.thumb_nail.url' %}" alt="My image" width="150"> </p>
<p>{{row.category.all|join:", "}} </p>
{%endfor%}
</div>
and my layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width , initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>
{{title}}
</title>
<!--the below line is for the css bootstrap, from cdnjs official site-->
<!--link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.1/css/bootstrap.min.css"-->
</head>
<body>
{% block content %} {% endblock %}
</body>
</html>

How to resolve Django model form does not saving

Hello I am trying to implement a Django model form when I try to now submit the form it does not save my data can I please get help I do not know where I can be going wrong with this implementation:
Code Below: models.py
class Videos(models.Model):
lecturer = models.CharField(max_length=100, blank=False, null=False)
module = models.CharField(max_length=100, blank=False, null=False)
video = models.FileField(upload_to='lectures/')
date = models.DateField(default=datetime.datetime.now())
Code Below: form.py
class LectureVideos(forms.ModelForm):
class Meta:
model= Videos
fields = '__all__'
Code Below:view.py
def LectureVideoForm(request):
form = LectureVideos()
if form.is_valid():
form.save()
return redirect('upload-success')
return render(request, 'forms.html', {'form':form})
Code Below:forms.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Upload Video">
</form>
</body>
</html>
You never passed the data to the form. In case of a POST request, you pass the request.POST (and request.FILES) to the form:
def LectureVideoForm(request):
if request.method == 'POST':
form = LectureVideos(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('upload-success')
else:
form = LectureVideos()
return render(request, 'forms.html', {'form':form})
Note: Functions are normally written in snake_case, not PerlCase, therefore it is
advisable to rename your function to lecture_video_form, not LectureVideoForm.

Having two forms in one HTML page

I wanted to have a page for both sign up and login. However I couldn't handle the two forms. Here is my code.
I was wondering myself if it is possible to give names to the forms or handle it in another way?
forms.py
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
class UserCreateForm(UserCreationForm):
class Meta:
fields = ("username", "email", "password1", "password2")
model = get_user_model()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["username"].label = "Display name"
self.fields["email"].label = "Email address"
url.py
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from . import views
app_name = 'accounts'
urlpatterns = [
url('', views.SignUp.as_view(), name="signup"),
url('', auth_views.LoginView.as_view(template_name="index.html"),name='login'),
url('', auth_views.LogoutView.as_view(), name="logout"),
]
index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div class="container">
<h1>Login</h1>
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" class="btn btn-default">
</form>
</div>
<div class="container">
<h1>Sign Up</h1>
<form method="POST" >
{% csrf_token %}
{{ form }}
<input type="submit" class="btn btn-default">
</form>
</div>
</body>
</html>
Thank You very much
I believe this post has the answer you need. Here are my thoughts on the information:
Put different URLs in the action for the two forms. Then you'll have two different view functions to deal with the two different forms. This will sometimes be a bit messy as some CBVs require a primary key and others do not, which may lead to conflicts.
Read the submit button values from the POST data. You can tell which submit button was clicked: How can I build multiple submit buttons Django form? This is the one that I prefer as it's cleaner to implement.
Actually i want able to POST and my apologies. in short you have to use Fetch API for this