I extended standart django user model by one-to-one field. Made news block, and added comments there. In comments i cant display user avatar from UserProfile model, cause dont understand how correctly ask database for it D;. Here my code:
main/models.py
from django.db import models
from django.utils import timezone
from django.contrib import auth
from django.contrib.auth.forms import User
from django.shortcuts import render, redirect
from profiles.models import UserProfile
# Create your models here.
class News(models.Model):
news_title = models.CharField(max_length=250)
news_body = models.TextField(max_length=2000, blank=True)
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
image = models.FileField()
published_date = models.DateTimeField(blank=True, null=True)
def publish(self, request):
self.published_date = timezone.now()
self.save()
return redirect('index')
def __str__(self):
return self.news_title
class Comment(models.Model):
news = models.ForeignKey('main.News', related_name='comments',
on_delete=models.CASCADE)
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
profiles/models.py
class UserProfile(models.Model):
JEW_CHOICE = (
('Да', 'Да'),
('Нет', 'Нет'),
)
MF_CHOICE = (
('М', 'М'),
('Ж', 'Ж')
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
country = models.CharField(max_length=100, default='', blank=True)
city = models.CharField(max_length=100, default='', blank=True)
description = models.CharField(max_length=500, default='', blank=True)
website = models.URLField(default='', blank=True)
avatar = models.ImageField(default='', blank=True)
gender = models.CharField(max_length=100, choices = MF_CHOICE, default = 'М', blank=True)
jew = models.CharField(max_length=100, choices = JEW_CHOICE, default = 'Да', blank=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.get_or_create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
#property
def avatar_url(self):
if self.avatar and hasattr(self.avatar, 'url'):
return self.avatar.url
main/views.py (meme_detail is the view, where should be comments with user info)
def meme_detail(request, pk):
news = get_object_or_404(News, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.user
comment.news = news
comment.save()
return redirect('main:meme_detail', pk=news.pk)
else:
form = CommentForm()
return render(request, 'main/meme_detail.html', {'news': news, 'form': form,})
meme_detail.html (news template with comments)
{% extends 'main/base.html' %}
{% block body %}
<h2>{{news.news_title}}</h2>
<img src='{{news.image.url}}' name='image' width='500px;'><br>
{{news.news_body}} <br><br>
<div class="row">
<div class="col">
<b>{{news.author}}</b>
</div>
<div class="col">
<i>{{news.published_date}}</i>
</div>
</div>
<div class="underline"></div>
<h3>Комментарии:</h3><br>
{% for comment in news.comments.all %}
<div class="row">
<div class="col-"><img src="{{ userprofile.avatar.url }}" alt="user-avatar" width="100px" height="100px"></div>
<div class="col">{{ comment.text }}</div>
</div>
<div class="row">
<div class="col"><strong>{{ comment.author }}</strong></div>
<div class="col">{{ comment.created_date}}</div>
</div>
<div class="underline"></div>
<br>
{% empty %}
<p>Пока ещё нет комментариев :(</p>
{% endfor %}
{% if request.user.is_authenticated %}
<div class="row">
<form method="POST">
{% csrf_token %}
{{form.text}}<br><br>
<a class="btn btn-success" href="{% url 'main:meme_detail' pk=news.pk %}"><button class='btn btn-success'>Добавить коммент! </button></a>
</form>
</div>
{% else %}
<i>Вы не можете писать комментарии, необходимо зарегистрироваться!</i>
{% endif %}
{% endblock %}
So, in this template, where "userprofile.avatar.url" should be object reference on User avatar. I tryed a lot of things, but it always the same:not displaying
You should do:
<img src="{{ comment.author.userprofile.avatar.url }}" alt="user-avatar" width="100px" height="100px">
Your comment has a foreign key to User (author), and User has a one to one field to UserProfile, which is the one that has the avatar attribute.
Also another tip:
You shouldn't really reduce the image in CSS (width: 100px; height: 100px;), but instead use a tool that allows you to create thumbnails of images. I use sorl-thumbnail and can't recommend it enough.
The reason is that if every user uploads a 1000x1000 image, you are downloading those big images that you don't really need, hence your site will be slower.
Maybe you should try accesing the User object in the template, not the Userprofile.
<img src="{{ user.userprofile.avatar.url }}" ...
Related
I am creating a blog for a project and I am having problem getting my comments to post to the back end.
My code is as follows:
models.py
from django.contrib.auth.models import User
from products.models import Category
class Post(models.Model):
"""Model to create blog posts"""
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
title = models.CharField(max_length=250)
body = models.TextField(blank=True, null=True)
image = models.ImageField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
class Comment(models.Model):
"""Model to handle user comments"""
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created']
def __str__(self):
return self.body[0:50]
forms.py
from .models import Post, Comment
class PostForm(ModelForm):
"""
Form to allow site owner to create a new blog post
"""
class Meta:
model = Post
fields = ['category', 'title', 'body', 'image']
class CommentForm(ModelForm):
"""Form to handle user comments"""
class Meta:
model = Comment
fields = ('body',)
views.py
def add_comment(request):
"""Method to add comments to a blog post"""
post = get_object_or_404(Post, post_id)
comments = post.comments.all()
new_comment = None
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
template = 'blog/post_detail.html'
context = {
'post': post,
'comments': comments,
'new-comment': new_comment,
'comment_form': comment_form,
}
return render(request, template, context)
urls.py
from . import views
urlpatterns = [
path('', views.view_blog, name="blog"),
path('<int:post_id>/', views.post_detail, name="post_detail"),
path('add_post/', views.add_post, name="add_post"),
path('edit_post/<int:post_id>/', views.edit_post, name="edit_post"),
path('delete/<int:post_id>/', views.delete_post, name="delete_post"),
path('add_comment/', views.add_comment, name="add_comment"),
path('delete/comment/<int:comment_id>/', views.delete_comment, name="delete_comment"),
]
Could anyone see where I am going wrong please? I have been playing around and getting different type of name and value error and am not getting any further forward.
Current template
<div id="comment-input">
{% if request.user.is_authenticated %}
<form action="{% url 'add_comment' %}" method="POST">
{% csrf_token %}
<div class="w-100 mb-2">
{{ comment_form | crispy }}
<button class="form-control btn btn-black border border-black rounded-0" type="submit">Submit
</button>
</div>
{% endif %}
</div>
<div class="comment-wrapper">
{% for comment in post.comments.all %}
<div id="comment-details">
<div id="comment-author">
{{comment.author}} - {{comment.created|timesince}} ago
</div>
<div id="comment-body">
{{comment.body}}
</div>
</div>
<div id="delete-comment">
{% if request.user == comment.author %}
Delete Comment
{% endif %}
</div>
<hr>
{% endfor %}
</div>
I have a problem with creating a profile instance in Django. when I try to update a profile the images of the profile don't save in the database My guess is that the form.save(commit=False) does not upload the photo nor update the field as it should but I do not understand why :
here is my code:
models.py
class Profile(models.Model):
user = models.OneToOneField(User, primary_key =True, on_delete=models.CASCADE, related_name= 'profile')
image = models.OneToOneField(UserImage, on_delete=models.SET_NULL, null=True, blank=True)
phone_number = models.CharField(max_length=50, null = True, blank = True)
followers = models.ManyToManyField(User, related_name='follower', blank=True)
following = models.ManyToManyField(User, related_name='following', blank=True)
biography = models.TextField(max_length=250, null=True, blank=True)
class UserImage(models.Model):
avatar = models.ImageField(blank=True, null=True,upload_to='avatar_pic')
header_image = models.ImageField(blank=True, null=True,upload_to='header_pic')
forms.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = (
'phone_number',
'biography',
)
class ImageProfileForm(ModelForm):
class Meta:
model = UserImage
fields = (
'avatar',
'header_image',
)
views.py
#login_required
def CompleteSignUp(request):
if request.method == 'POST':
profile_form = ProfileForm(request.POST,request.FILES ,instance=request.user.profile)
image_profile_form = ImageProfileForm(request.POST, instance=request.user.profile.image)
if profile_form.is_valid() and image_profile_form.is_valid():
profile = profile_form.save(commit=False)
images = image_profile_form.save()
profile.user = request.user
profile.social = social
profile.image = images
profile_form.save()
return redirect('blog:Home')
else:
profile_form = ProfileForm(
initial={
'phone_number':request.user.profile.phone_number,
'biography':request.user.profile.biography
}
)
if request.user.profile.image:
image_profile_form = ImageProfileForm(
initial={
'avatar':request.user.profile.image.avatar,
'header_image':request.user.profile.image.header_image
}
)
else:
image_profile_form = ImageProfileForm()
return render(request, 'user/createprofile.html', {'form_p': profile_form, 'form_i': image_profile_form})
templates
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{ form_p.biography }}
biography
</div>
<div>
{{ form_p.phone_number }}
phone_number
</div>
<div>
{{ form_i.avatar }}
avatar
</div>
<div>
{{ form_i.header_image }}
header
</div>
<br>
<input type="submit" value="Register">
</br>
</form>
{% for field in form_s %}
{% for error in field.errors %}
<p style="color: rgba(255, 0, 0, 0.678)">{{ error }}</p>
{% endfor %}
{% endfor %}
{% endblock %}
profile save but the image of the profile doesn't save what's my fault.
...
Change:
profile.user = request.user
profile.social = social
profile.image = images
profile_form.save()
To:
profile.user = request.user
profile.social = social
profile.image = images
profile.save() # Use this
You need to save profile instead of the form with commit=False, because you did custom stuff to profile.
It is a Medical Lab Software solution. A user makes lab investigation request that is unique to a patient’s encounter, I.e., a patient can have 1 or more request per encounter. This uniqueness is denoted by the encounter_id.
The challenge now is the ability to send the results back to the requester.
I am able to display all requests per encounter_id on a template but unable to return the result since each result is tied to a particular investigation. This is largely because I have a limited knowledge on JS. My current approach can only submit one record, usually the last record
Here’s the URL that displays the result template: https://smart-care.herokuapp.com/labs/lab_results/1/
Here’s the django template:
<div class="container">
<form id="myForm" method="POST" action="">{% csrf_token %}
{% for request in lab_request %}
{{ request.test }}: <input type="text" class="result" id="{{ request.test.id }}" name="test_id"> <br>
{% endfor %}
<br><br>
<button>Send Result</button>
</form>
</div>
# Here’s my view:
def lab_results_view(request, enc_id):
lab_request = LabRequest.objects.filter(encounter_id=enc_id, done=False, decline=False)
if request.POST.get('test_id'):
for req_id in request.POST:
results = request.POST.get(req_id, False)
print("results = ",results)
template = "labs/lab_results.html"
context = {"lab_request":lab_request}
return render(request, template, context)
The models:
class LabRequest(models.Model):
encounter = models.ForeignKey(PatientEncounter, on_delete=models.CASCADE, blank=True, null=True)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, blank=True, null=True)
test = models.ForeignKey(LabTest, on_delete=models.CASCADE)
accepted = models.BooleanField(default=False)
decline = models.BooleanField(default=False)
done = models.BooleanField(default=False)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return str(self.patient.id)
class LabResult(models.Model):
lab_request = models.ForeignKey(LabRequest, on_delete=models.CASCADE, blank=True, null=True)
result = models.CharField(max_length=225)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return str(self.result)
Thanks as I anticipate your kind response.
The problem that Django handles form elements based on name attribute and not the id attribute so this means you need to change name per request and on the view side you loop on all keys in request.POST like
for req_id in request.POST:
result = Request.POST[req_id]
Edit: for the template
<div class="container">
<form id="myForm" method="POST" action="">{% csrf_token %}
{% for request in lab_request %}
{{ request.test }}: <input type="text" class="result" id="{{ request.test.id }}" name="{{ request.test.id }}">
<br> {% endfor %} <br><br>
<button>Send Result</button>
</form> </div>
Edit2: The view shall be like this
def lab_results_view(request, enc_id):
lab_request = LabRequest.objects.filter(encounter_id=enc_id, done=False, decline=False)
if request.method == "POST":
for req_id in request.POST:
results = request.POST.get(req_id)
print("results = ",results)
template = "labs/lab_results.html"
context = {"lab_request":lab_request}
return render(request, template, context)
I build a model for user reviews and rating. And made the form for my model , then I call the form in my views . When i click on button from detail page to get the "rate.html" it get me there but did not save the data from there and give me this error .
IntegrityError at /product/new-shoes/rate/
NOT NULL constraint failed: products_review.user_id
my models.py is:
class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product = models.ForeignKey(Product , on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
text = models.TextField(max_length=3000 , blank=True)
rate = models.PositiveSmallIntegerField(choices=RATE_CHOICES)
likes= models.PositiveIntegerField(default=0)
dislikes = models.PositiveIntegerField(default=0)
def __str__(self):
return self.user.full_name
my forms.py is:
class RateForm(forms.ModelForm):
text = forms.CharField(widget=forms.Textarea(attrs={'class':'materialize-textarea'}),required=False)
rate = forms.ChoiceField(choices=RATE_CHOICES, widget=forms.Select(),required=True)
class Meta:
model = Review
fields= ('text', 'rate')
my views.py is:
class RateView(CreateView):
form_class = RateForm
template_name = 'rate.html'
def form_valid(self, form):
form.instance.product = Product.objects.get(slug=self.kwargs['slug'])
return super().form_valid(form)
def get_success_url(self):
return reverse('products:detail', kwargs={'slug': self.object.product.slug})
and my rate.html is:
{% extends "base.html"%}
{% block content %}
<form method="POST" action="" role="form" class="col s12">
{% csrf_token %}
<div class="input-field col s12">
{{ form.rate }}
</div>
<div class="input-field col s12">
{{ form.text }}
<label for="textarea1">Opinion</label>
</div>
<button type="submit" name="action" class="waves-effect waves-light btn"><i class="material-icons left">star</i>Rate</button>
</form>
{% endblock %}
my urls.py for the view is:
path('<slug:slug>/rate/', RateView.as_view(), name='rate-product1'),
The error message is telling you that the user field is not being set. You can do this in the form_valid function:
def form_valid(self, form):
...
form.instance.user = self.request.user
form.instance.product = Product.objects.get(slug=self.kwargs['slug'])
...
return super().form_valid(form)
Try to use this :-
class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product = models.ForeignKey(Product , on_delete=models.CASCADE,null=True)
date = models.DateTimeField(auto_now_add=True)
text = models.TextField(max_length=3000 , blank=True)
rate = models.PositiveSmallIntegerField(choices=RATE_CHOICES)
likes= models.PositiveIntegerField(default=0)
dislikes = models.PositiveIntegerField(default=0)
def __str__(self):
return self.user.full_name
What i have changed :-
I have set null = True in your product variable, which is with ForeignKey
This Code is working perfectly. The only thing I want to change is submit button "Vote" into "Voted" after user voted the option ,instead of displaying error message " You Already Voted".So that the user can know which options he voted already when he logs in to vote the option next time
urls.py
path('<slug>/',views.options,name='options'),
path('<slug>/vote/', views.vote, name='vote'),
models.py
class Category(models.Model):
name = models.CharField(max_length=250)
slug = AutoSlugField(populate_from='name')
details = models.TextField(blank=True)
image = models.ImageField(blank=True,upload_to='categories')
views = models.IntegerField(default=0)
created = models.DateTimeField(auto_now=True)
modified = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=True)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "Categories"
class Option(models.Model):
name = models.CharField(max_length=250)
slug = AutoSlugField(populate_from='name')
image = models.ImageField(blank=True,upload_to='options')
details = models.TextField()
category = models.ForeignKey(Category, on_delete=CASCADE)
votes = models.IntegerField(default=0)
active = models.BooleanField(default=True)
def __str__(self):
return self.name
class Vote(models.Model):
option = models.ForeignKey(Option, on_delete=CASCADE)
voter = models.ForeignKey(User, on_delete=CASCADE)
slug = AutoSlugField(populate_from='option')
def __str__(self):
return self.voter
views.py
def options(request,slug):
category = Category.objects.get(slug=slug)
category.views += 1
category.save()
options = category.option_set.all().order_by('-votes')
if request.method == "POST":
if request.user.is_authenticated:
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = category
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
else:
messages.error(request, 'You have to login first to give comment')
return redirect('rank:login')
else:
form = CommentForm()
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
def vote(request,slug):
if request.user.is_authenticated:
option = Option.objects.get(slug=slug)
category = option.category
if Vote.objects.filter(slug=slug,voter_id=request.user.id).exists():
messages.error(request,'You Already Voted!')
return redirect('rank:options', category.slug)
else:
option.votes += 1
option.save()
voter = Vote(voter=request.user,option=option)
voter.save()
messages.success(request,'Voted.{} peoples also agree with you.'.format(option.votes-1))
return redirect('rank:options',category.slug)
else:
messages.error(request,"You have to login first to vote.")
return redirect('rank:login')
options.html
<ol type="1">
<center>{% bootstrap_messages %}</center>
{% for option in options %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card h-100">
<div class="card-body">
<b><li>
<img src="/media/{{option.image}}" width="400" height="300">
<h4>{{option.name}}
</h4>
<h5 class="card-text">{{ option.details}}</h5>
<h5>{{ option.votes }} votes</h5>
<form action="{% url 'rank:vote' option.slug %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-success" value="Vote" >
</form>
</li></b>
</div>
<div class="card-footer">
<small class="text-muted"></small>
</div>
</div>
</div>
{% empty %}
<div class="card w-100">
<div class="card-body">
<h4>Item not available</h4>
</div>
</div>
{% endfor %}
</ol>
The simplest way to achieve this might be to add an attribute to each option to indicate whether the currently logged in user has voted on that option before you pass the options to the template.
For example:
def options(request,slug):
category = Category.objects.get(slug=slug)
category.views += 1
category.save()
options = category.option_set.all().order_by('-votes')
# Indicate whether the user has voted or not
for option in options:
option.has_voted = option.vote_set.filter(voter=request.user).exists()
...
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
And then you can check the has_voted attribute in the template when you render the button:
{% if option.has_voted %}
You already voted
{% else %}
<input type="submit" class="btn btn-success" value="Vote" >
{% endif %}