How can I add a image on a post in django? - python

I want to create an upload image button in django admin post models. When an image is uploaded, will be nice to be displayed on both blog card and then on post detail on the website. Here is my code until now. How should I do this in order to make this work?
Here is my blog.html page
<div class="container">
<div class="row">
<!-- Blog Entries Column -->
<div class="column">
{% for post in post_list %}
<div class="card mb-4">
<div class="card-body">
<img class="card-image">{{post.header_image}}</img>
<h2 class="card-title">{{ post.title }}</h2>
<p class="card-text text-muted h6">{{ post.author }} | {{ post.created_on}} </p>
<p class="card-text">{{post.content|slice:":200"}}</p>
Află mai multe
</div>
</div>
{% endfor %}
</div>
</div>
</div>
Here is my post detail.html
<div class="container">
<div class="detail-row">
<div class="card-detail-body">
<h1> {{ post.title }} </h1>
<p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p>
<p class="card-text ">{{ post.content | safe }}</p>
</div>
</div>
</div>
Here is models.py
from django.db import models
import datetime
from django.contrib.auth.models import User
STATUS = ((0, "Draft"), (1, "Published"))
class Post(models.Model):
title = models.CharField(max_length=1048)
slug = models.SlugField(max_length=1048)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
related_name=('blog_posts')
content = models.TextField()
status = models.IntegerField(choices=STATUS, default=0)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
And here are views.py
def blogPage(request):
floaterForm = FloaterForm()
post_list = Post.objects.all().order_by('-created_on')
context = {
'flForm' : floaterForm,
"post_list": post_list
}
return render(request, "blog.html", context)
def post_detail(request, pk):
post= Post.objects.get(pk=pk)
context = {
'post' : post
}
return render(request, "post_detail.html", context)

You've to create one field inside your models.py file like this
image = models.ImageField(upload_to="your_upload_dir_name")
then you've to set your media configuration now you can access your image inside your template like this
<img src="{{ post.image.url }}">

You firstly have to create the image field in Django. For example
blog_image = models.ImageField(upload_to="/media/blog_images")
#This /image/blog_images is the image directory.
It will save the image URL in the model field. Then you can use this image URL in the src of the image tag.
The html code should be like this.
<form method = "post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
And in the views, your code will be like this. You can change it according to your configuration.
if request.method == 'POST':
form = BlogsForm(request.POST, request.FILES)
if form.is_valid():
form.save()
Then you have to set the media configuration in your server configuration and in settings.py. I mostly use Nginx, so I do this.
#settings.py
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
#ngnix
location /media {
autoindex on;
alias /path to media directory of project;
}
If still you got confuse, tell me in the comments. Thanks

Related

How to write an image attribute file in django (The 'image' attribute has no file associated with it)

I am building a django blog website and I am trying to upload an image on the website. I can access the photos on the admin page but whenever I try to access it on the page I get an enroll. The trace back says
"
The 'image' attribute has no file associated with it.
This my code for the model
class article(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
image = models.ImageField(null=True, blank=True, upload_to='static/media/article')
date = models.DateTimeField(default=timezone.now)
def __str__ (self):
return self.title
def get_absolute_url(self):
return reverse('article-detail', kwargs= {'pk':self.pk})
This is my views code
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = article
fields= ['title', 'content', 'image']
def ArticleCreateView(request):
if request.method == "POST":
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user.id
article.save()
return HttpResponse
this is the blog template code
{% extends "agri_tourism/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<section class="flex-container">
<div>
{% for article in articles %}
<article class="media content-section">
{% load static %}
<div class="media-body">
<div style="text-decoration:none; display: inline-block;" class="article-metadata">
<img class="rounded-circle article-img" src="{{user.profile.image.url}}">
<a style="text-decoration:none;" class="mr-2" href="#">{{ article.author }}</a>
<small class="article-date">{{ article.date|date:"F d, Y" }}</small>
</div>
<h2><a class="article-title" href="">{{ article.title }}</a></h2>
<p class="article-content">{{ article.content }}</p>
<img class="image-content" id="image-el" src = "{{article.image.url}}">
<div class="like-share-btns">
</div>
</article>
{% endfor %}
The form page code is below
{% extends "agri_tourism/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class = "content-section">
<div class = "container">
<div class = "row md-8">
<form method = "POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class = "form-group">
<legend class ="border-bottom mb-4">Blog article</legend>
{{form|crispy }}
{{form.media}}
</fieldset>
<div class= "form-group">
<button class="btn btn-outline-info" type="submit" style= "margin-top:4px; ">Submit the Article</button>
</div>
</form>
</div>
</div>
</div>
I want to access photos on the blog page
check the image with if condition like this...
<div class="media-body">
<div style="text-decoration:none; display: inline-block;" class="article-metadata">
{% if user.profile.image.url %}
<img class="rounded-circle article-img" src="{{user.profile.image.url}}">
{% endif %}
<a style="text-decoration:none;" class="mr-2" href="#">{{ article.author }}</a>
<small class="article-date">{{ article.date|date:"F d, Y" }}</small>
</div>
and also check those settings
urls.py (in project directory)
from django.conf import settings # --------> this
from django.conf.urls.static import static # --------> this
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')),
]+static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # --------> this
settings.py (in project directory)
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

How to go to a page with single post from the news list

Can you help me with my project.
I 'm trying to go from the page with all the news to a single post, but whatever i stay in page with all news.
But if I write the address o in the address bar, everything works.
Models.py
class News(models.Model):
title = models.CharField(max_length=1000, verbose_name='Название')
slug = models.SlugField(max_length=200, db_index=True)
image = models.ImageField(upload_to='articles/', verbose_name='Фото')
publish = models.DateTimeField(default=timezone.now, verbose_name='Дата публикации')
author = models.ForeignKey(User, related_name='news', on_delete=models.CASCADE, verbose_name='Автор', null=True)
text = models.TextField(verbose_name='Текст')
tags = models.ManyToManyField(Tag, related_name='news', verbose_name='Тэг')
created = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
updated = models.DateTimeField(auto_now=True, verbose_name='Дата обнавления')
status = models.CharField(max_length=30, choices=STATUS_CHOICES, default='опубликован', verbose_name='Статус')
class Meta:
ordering = ('title',)
verbose_name = 'Новости'
verbose_name_plural = 'Новости'
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post', kwargs={'post_slug': self.slug})
Views.py
def list_news(request):
news = News.objects.all()
return render(request,
'diplom/news/post.html',
{'news': news})
def single_news(request, post_slug):
post = get_object_or_404(News, slug=post_slug)
return render(request,
'diplom/news/blog-single.html',
{'post': post})
urls.py
urlpatterns = [
path('news/', views.list_news, name='News'),
path('post/<slug:post_slug>/', views.single_news, name='post')
]
templates
<div class="col-lg-4 col-md-6 grid-item">
{% for news in news %}
<div class="blog-item large-item set-bg">{{ news.image }}
<a href="{{post.get_absolute_url}}" methods="post" >
<div class="categories">{{ news.title }}</div>
<h5>{{ news.text| linebreaks|truncatechars:200 }}</h5>
</a>
<div>
{{ news.publish }}
{{ news.tag }}
</div>
</div>
{%endfor%}
</div>
I use the {% url 'url-name' param1 %} for my templates. Does this work when you use it in your template?
URL Reversing
<div class="col-lg-4 col-md-6 grid-item">
{% for post in news %}
<div class="blog-item large-item set-bg">{{ post.image }}
<a href="{% url 'post' post.slug %}">
<div class="categories">{{ post.title }}</div>
<h5>{{ post.text|linebreaks|truncatechars:200 }}</h5>
</a>
<div>
{{ post.publish }}
{{ post.tag }}
</div>
</div>
{%endfor%}
</div>
Also see the name of your single object is the same as your list of objects.
{% for news in news %}
You could rename to post, then change the rest of the variables (not the link) and it should work.
<div class="col-lg-4 col-md-6 grid-item">
{% for post in news %}
<div class="blog-item large-item set-bg">{{ post.image }}
<a href="{{ post.get_absolute_url }}">
<div class="categories">{{ post.title }}</div>
<h5>{{ post.text|linebreaks|truncatechars:200 }}</h5>
</a>
<div>
{{ post.publish }}
{{ post.tag }}
</div>
</div>
{%endfor%}
</div>
</div>
I also updated
<a href="{% url 'post' news.slug %}" methods="post" >
to
<a href="{% url 'post' news.slug %}">
as you are just retrieving the object and not sending information to update it.
Thanks for the answers. The error was in this line.
<a href="{{ post.get_absolute_url }}">
It was necessary to do this
<a href="{{news.get_absolute_url }}">

Django form_valid() in CreateView not executing when trying to create a new dish object

In this code, the form_valid() function inside DishCreateView is not being executed when I try to create a new dish ie I am unable to create a new dish, when I click on create button page url not changes and remains the same. For detecting this I have put print statement inside form_valid() but this is not executed. Please help me out. Thanks
models.py
class Dish(models.Model):
name = models.CharField(max_length=100)
image = models.ImageField(upload_to='dishes_pics')
description = models.TextField()
ingredients = models.TextField(blank=True)
required_time_to_cook = models.CharField(max_length=100)
favourite = models.ManyToManyField(User, related_name='favourite', blank=True)
cuisine_id = models.ForeignKey(Cuisine, on_delete=models.CASCADE)
user_id = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('dish-detail', kwargs={'pk': self.pk})
def save(self):
super().save()
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300,300)
img.thumbnail(output_size)
img.save(self.image.path)
views.py
class DishCreateView(LoginRequiredMixin,CreateView):
model = Dish
fields = ['name', 'image', 'description','required_time_to_cook','cuisine_id','user_id']
def form_valid(self, form):
form.instance.user_id = self.request.user
print('self==========form==create=====',form)
return super().form_valid(form)
form_template.html
{% extends "cookbook/base.html"%}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">
Add Dish
</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Create</button>
</div>
</form>
</div>
{% endblock content %}
dish_detail.html
{% extends 'cookbook/base.html' %}
{% load static %}
{% block content %}
<link rel="stylesheet" type="text/css" href="{% static 'cookbook/dish.css' %}">
<div class="media">
<img class="rounded-circle account-img" src="{{ object.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ object.name }}</h2>
<p class="text-secondary">{{ object.description }}</p>
<p class="text-secondary">{{ object.required_time_to_cook }}</p>
<p class="text-secondary"> {{object.cuisine_id}}</p>
</div>
</div>
{% endblock content %}
First thing i notice is that the name of the template is called form_template.html. Accourding the docs the createview uses a default suffix value for naming them template '_form' this in combination with you model name. So you should change your template name to:
from : form_template.html
to : dish_form.html
I think your view does not receive the data you post because the naming is not correct and you didn't add an action value to the form. This should not be problem if the naming matches the view.
If this still not fixes your problem step 2 would be to add a
def form_invalid(self, form):
print(form.errors)
to your view and put a print statement in there
this would give you the error back with the problem. so you know where the problem is.

Display user's posts in their profile on django

I have been trying to make the profile page of a user have the user's posts but everytime I run my code nothing shows up only the user's username and user's email. I've tried multiple ways to make it work but somehow it doesn't. I think the profile.html has the error in it.
views.py
def profile(request, pk=None):
if pk:
post_owner = get_object_or_404(User, pk=pk)
user_posts=Post.objects.filter(posti=request.user)
else:
post_owner = request.user
user_posts=Post.objects.filter(posti=request.user)
return render(request, 'profile.html', {'post_owner': post_owner, 'user_posts': user_posts})
models.py
class Post(models.Model):
text = models.CharField(max_length=200)
posti = models.ImageField(upload_to='media/images', null=True, blank="True")
user = models.ForeignKey(User, related_name='imageuser', on_delete=models.CASCADE, default=2)
profile.html
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ post_owner.username }}</h2>
<p class="text-secondary">{{ post_owner.email }}</p>
</div>
{% for Post in user_posts %}
<li class="list-group-item">{{ Post.text }}
{{ Post.user }}
{% if Post.posti %}
<img src="{{ image.posti.url }}" alt="image here" style="max-width: 300px; max-height: 300px">
{% endif %}
{% if Post.user == user %}
<div class="float-right">
<form action="delete_image/{{ image.id }}/" action="post">
<button type="submit" class="btn btn-outline-danger btn-sm">Delete</button>
</form>
</div>
{% endif %}
</li>
{% endfor %}
</div>
</div>
The problem here is with the line:
user_posts=Post.objects.filter(posti=request.user)
To get the posts from the logged in user you will need to use this:
user_posts=Post.objects.filter(user=request.user)
and to get the posts from the selected user you will need to do this:
user_posts=Post.objects.filter(user_id=pk)
I hope this helps :)
You are querying the wrong column:
user_posts=Post.objects.filter(posti=request.user)
posti is an ImageField. You actually want to query the user field:
user_posts=Post.objects.filter(user=request.user)
That being said, you don't need any of these queries in your view. You can simply make use of your related_name, i.e.:
{% for Post in post_owner.imageuser.all %}

Django when clicked show another model attribute

I'm trying to make window where some text will be shown in one language, when clicked second language will appear. I've created one model which consists every information about post.
model.py
from django.db import models
from datetime import datetime
from django.contrib.auth.models import User
def get_default_user():
return User.objects.get(id=1)
class EveryPost(models.Model):
title_pl = models.CharField(max_length=100, blank=True)
title_ru = models.CharField(max_length=100, blank=True)
text_pl = models.TextField(blank=True)
text_ru = models.TextField(blank=True)
date = models.DateTimeField(default=datetime.now, blank=True)
User = models.ForeignKey(User, on_delete=models.CASCADE, default=get_default_user)
def __str__(self):
return self.title_pl
html
{% for obj in EveryPost %}
<div class="card text-center">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link nav-main" href="{% url 'rupl' obj.pk %}">PL</a>
</li>
<li class="nav-item">
<a class="nav-link nav-main" href="{% url 'plru' obj.pk %}">RU</a>
</li>
</ul>
</div>
<div class="card-body">
<h5 class="card-title">{{ obj.title_pl }}</h5>
<p class="card-text">{{ obj.text_pl|truncatechars:350 }}</p>
Zobacz
</div>
<div class="card-footer text-muted">
<span class="float-left">{{ obj.date|date:"d M y" }}</span>
<span class="float-right">Przesłane przez: {{ obj.User }}</span>
</div>
</div>
{% endfor %}
I've tried to make switch between text_pl and text_ru but it wasn't a good idea(lost data)
views.py
def plru(request, pk):
post = EveryPost.objects.get(pk=pk)
post.text_pl = post.text_ru
post.title_pl = post.title_ru
post.save()
return redirect('index')
def rupl(request, pk):
post = EveryPost.objects.get(pk=pk)
post.text_ru = post.text_pl
post.title_ru = post.title_pl
post.save()
return redirect('index')
Image explaining what i want to achieve
there's no need for lot's of functions and urls,
simple is better:
in views.py:
def post_detail(request, post_id):
post = get_or_404(Post, id= post_id)
return render(request, 'post_details.html")
in post_details.html:
<div id="post_in_1st_lng" style="visibility: visible;">
{{ post.title_pl }}
</div>
<div id="post_in_2nd_lng" style="visibility: hidden;">
{{ post.title_ru }}
</div>
<button id="lan_change">Change Language </button>
<script>
$("#lan_change").on('click', function (){
first_vis = $("#post_in_1st_lng").css('visibility') == "visible" ? "hidden" : "visible";
sec_vis = $("#post_in_2nd_lng").css('visibility') == "visible" ? "hidden" : "visible";
$("#post_in_2nd_lng").css('visibility', sec_vis);
$("#post_in_1st_lng").css('visibility', first_vis);
});
</script>
this is a simple jQuery solution.
another way is using ajax, but since the post object is the same one, so text in both languages are already loaded, so no need for time wasting server call.
I fixed it, perhaps it's not most efficient way, but works though :)
I add one extra element in model:
version_pl = models.BooleanField(default=True)
So in HTML it check whether is True or False:
<div class="card-body">
{% if obj.version_pl == False %}
<h5 class="card-title">{{ obj.title_ru }}</h5>
<p class="card-text">{{ obj.text_ru|truncatechars:350 }}</p>
{% else %}
<h5 class="card-title">{{ obj.title_pl }}</h5>
<p class="card-text">{{ obj.text_pl|truncatechars:350 }}</p>
{% endif %}
Zobacz całość
</div>
and views.py
def rupl(request, pk):
post = EveryPost.objects.get(pk=pk)
post.version_pl = True
post.save()
return redirect('index')
def plru(request, pk):
post = EveryPost.objects.get(pk=pk)
post.version_pl = False
post.save()
return redirect('index')
Thanks for replies

Categories