I am trying to create access my profile but one of my models attributes (profile_picture) is empty and it's causing my profile page to crash. I have set blank=True, and it was working earlier but it has stopped working since and i can't figure out why. If I go to the django admin and manually add a profile then I can visit my profile again and everything works. I guess my question is why can't I view my profile even if the profile_pic attribute is empty? Shouldn't blank=True take care of that?
models.py
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
profile_picture = models.ImageField(upload_to='profile_pics', blank=True)
def __str__(self):
return self.user.username
views.py
def edit_user_profile(request):
if request.method == 'POST':
form = EditUserProfileForm(request.POST, instance=request.user.userprofile)
if form.is_valid():
form.save()
return redirect('/accounts/profile')
else:
form = EditUserProfileForm(instance=request.user)
args = {'form': form}
return render(request, 'accounts/edit_user_profile.html', args)
Profile.html
<div class="container">
<br>
<h2>{{ user }}</h2>
<br>
<p>Name: {{ user.first_name }} {{ user.last_name }}</p>
<img src="{{ user.userprofile.profile_picture.url }}" width="240px">
<p></p>
<p>Motto: {{ user.userprofile.description }}</p>
<p>Youtube: {{ user.userprofile.website }}</p>
<p>About Me: {{ user.userprofile.city }}</p>
<p>Phone Number: {{ user.userprofile.phone }}</p>
Edit Profile<br>
<!-- if profile is updated succesfully -->
{% if messages %}
{% for message in messages %}
<br><br>{{ message }}
{% endfor %}
{% endif %}
</div>
Again, if I go the Django admin and manually upload a photo I can access my profile but by default it won't work without an image.
Any insight is appreciated
Thanks
It's because of the code {{ user.userprofile.profile_picture.url }} in template.
It always try to find picture's url even if your profile_picture is none.
Just add if/else in template, or add method if you have default image.
ie. if/else in template
{% if user.userprofile.profile_picture %}
<img src="{{ user.userprofile.profile_picture.url }}" width="240px">
{% else %}
<img src="{% static 'your_default_img_path' %}" width="240px">
{% endif %}
Or use method for picture
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
profile_picture = models.ImageField(upload_to='profile_pics', blank=True)
def __str__(self):
return self.user.username
def get_profile_picture(self):
if self.profile_picture
return profile_picture_url
else:
return 'your_default_img_url_path'
Try this code in views.py
form = EditUserProfileForm(request.POST, request.FILES)
if form.is_valid():
city = form.cleaned_data['city']
description = form.cleaned_data['description']
profile_picture = form.cleaned_data['profile_picture']
website = form.cleaned_data['website']
phone = form.cleaned_data['phone']
EditUser= UserProfile(city =city,
description=description,
profile_picture =profile_picture,
website =website,
phone =phone,
user_id=request.user)
EditUser.save()
return redirect('/accounts/profile')
else:
form = EditUserProfileForm(instance=request.user)
args = {'form': form}
return render(request, 'accounts/edit_user_profile.html', args)
Related
I am adding a simple review function on my Social platform Django project, where Users can write a review on another user's profile. But after posting the review, it's only showing on the profile page of the user that I'm currently signed in to.
This is my models.py
` class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
reviewmsg = models.TextField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.reviewmsg)`
views.py
` def userProfile(request, pk):
user = User.objects.get(id=pk)
rooms = user.room_set.all()
reviews = user.review_set.all()
if request.method == 'POST':
review = Review.objects.create(
user=request.user,
reviewmsg=request.POST.get('reviewmsg')
)
return redirect('user-profile', pk=user.id)
context = {'user':user, 'rooms':rooms, 'reviews':reviews}
return render(request, 'pages/profile.html', context)`
excerpt from my profile page template
` <div>
{% for review in reviews %}
<div class="comment-sect" style="margin-top: 0.5rem;">
<div class="comment-photo" style="margin: auto;">
<a href="{% url 'user-profile' review.user.id %}">
<div class="profile-photo">
<img src="{{review.user.avatar.url}}">
</div>
</a>
</div>
<div class="comment-info" style="width: 300px;">
<small>#{{review.user}}</small>
<small>{{review.created|timesince}} ago </small>
<p style="margin-bottom:0;">{{review.reviewmsg}}</p>
</div>
</div><!--end of comment-sect-->
{% endfor %}
<div class="comment-form" style="margin-top: 0.5rem; text-align:center;">
<form method="POST" action="">
{% csrf_token %}
<div>Submit a Review
<input type="text" name="reviewmsg" placeholder="What do you think about this User?"/>
</div>
</form>
</div>`
EDITED
As mentioned by #lain Shelvington, I agree that I need to add another ForeignKey to my models. I tried updating the models.py to:
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=True)
reviewmsg = models.TextField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.reviewmsg)
and views.py to:
user = User.objects.get(id=pk)
rooms = user.room_set.all()
reviews = user.review_set.all()
if request.method == 'POST':
review = Review.objects.create(
user=request.user,
profile=user,
reviewmsg=request.POST.get('reviewmsg')
)
return redirect('user-profile', pk=user.id)
context = {'user':user, 'rooms':rooms, 'reviews':reviews}
return render(request, 'pages/profile.html', context)
but ended up with error:
ValueError at /profile/7/
Cannot assign "<User: celeste>": "Review.profile" must be a "Profile" instance
Homefeed is the page where i query all the blogposts
In this project, any user that sees a blogpost that they are interest in can submit their interest to the post. 1 user can only submit 1 interest to that blogpost, but they can submit as many interest as they want to different blogposts.
Right now in my home.html, I am trying to make it such that if YOU have submitted interest,(aka your interest status is at pending or accept or decline) for that particular blog post, you will see the view interest button instead of the submit interest button.
But I am facing a problem because in my views, I am querying for blog_posts = BlogPost.objects.all() and not blog_post = get_object_or_404(BlogPost, slug=slug). As such, how am I able to query whether or not for the particular blogpost, the user has already submitted an interest in my template to determine which button should show in my home.html? Thanks, and also I dont want to change the url at all :)
views.py
def home_feed_view(request, *args, **kwargs):
context = {}
blog_posts = BlogPost.objects.all()
context['blog_posts'] = blog_posts
page = pageFilter(request.GET, queryset=BlogPost.objects.exclude(author_id=request.user.id).order_by('date_updated'))
context['page'] = page
paginated_page = Paginator(page.qs, 4)
page = request.GET.get('page')
page_obj = paginated_page.get_page(page)
context['page_obj'] = page_obj
return render(request, "HomeFeed/snippets/home.html", context)
home.html
{% for post in page_obj %}
{% if post.interest_set.exists and request.user.is_authenticated %}
<a class="btn btn-info btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">View Interest</a>
{% else %}
<a class="btn btn-warning btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">Submit Interest</a>
{% endif %}
{% endfor %}
urls.py
path('', home_feed_view , name= "main"),
models.py
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
class BlogPost(models.Model):
title = models.CharField(max_length=50, null=False, blank=False, unique=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
class Interest(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
blog_post = models.ForeignKey(BlogPost, on_delete=models.CASCADE)
class InterestInvite(models.Model):
ACCEPT = "ACCEPT"
DECLINE = "DECLINE"
PENDING = "PENDING"
STATUS_CHOICES = [
(ACCEPT, "accept"),
(DECLINE, "decline"),
(PENDING, "pending"),
]
interest = models.OneToOneField(Interest, on_delete=models.CASCADE, related_name="interest_invite")
status = models.CharField(max_length=25, choices=STATUS_CHOICES, default=PENDING)
objects= models.Manager
views.py
type = TypeFilter(request.GET, queryset=BlogPost.objects.exclude((Q(author_id__in=request.user.blocked_users.all()) | Q(author = request.user))).order_by('date_updated')).annotate(user_has_interest=Case(When(interest__user=request.user, then=Value(True)), default=False, output_field=BooleanField()))
Using Django filters:
filters.py
class TypeofIdeaFilter(django_filters.FilterSet):
title = django_filters.CharFilter(field_name="title", lookup_expr='icontains')
class Meta:
model = BlogPost
You can annotate a field on your query:
from django.db.models import Case, When, Value, BooleanField
blog_posts = BlogPost.objects.all().annotate(
user_has_interest=Case(When(interest__user=request.user, then=Value(True)), default=False, output_field=BooleanField())
)
Now you can check in your template using if-else:
{% if post.user_has_interest %}
Something
{% else %}
Something else
{% endif %}
As Pierre mentioned template tags also achieve this (alternate for annotate answerd by Abdul).
Firstly create the file structure. Go into the app directory where the tag is needed, and add these files:
templatetags
templatetags/__init__.py
templatetags/blog_tags.py
The templatetags/blog_tags.py file:
from django import template
register = template.Library()
#register.simple_tag
def interest_submitted(blog_id, user_id):
if Interest.objects.filter(blog_post__id=blog_id, user_id=user_id).exists():
return True
else:
return False
In the template:
{% load blog_tags %} <!--don't forget to load the blog_tags.py file in the template. -->
{% for post in page_obj %}
{% interest_submitted post.id request.user.id as result %}
{% if result and request.user.is_authenticated %}
<a class="btn btn-info btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">View Interest</a>
{% else %}
<a class="btn btn-warning btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">Submit Interest</a>
{% endif %}
{% endfor %}
You can use a try instead of get_object_or_404(). That way you can use different logic and return different contexts if the object does not exist
pseudocode:
context = {}
try:
blog_posts = BlogPost.objects.all()
...
context = "Something"
return render(request, "HomeFeed/snippets/home.html", context)
except BlogPost.DoesNotExist:
context = "Something else"
return render(request, "HomeFeed/snippets/home.html", context)
form.is_valid() always fails. I tried different ways to handle it but fails every time and it returns false. Please help in figuring out whats wrong with the code.
models.py looks like this -
class Album(models.Model):
album_name = models.CharField(max_length=50, primary_key=True)
place = models.CharField(max_length=50)
date_pub = models.DateTimeField('date published')
def __str__(self):
return self.album_name
class Images(models.Model):
album_name = models.ForeignKey(Album, db_column='album_name')
image_name = models.CharField(max_length=40)
image = models.FileField(null=True, blank=True)
upload_dt = models.DateTimeField(auto_now=True, auto_now_add=False)
like_cntr = models.IntegerField(default=0)
description = models.CharField(max_length=200, null=True)
def __str__(self):
return self.image_name
forms.py is -
class ImagesForm(forms.ModelForm):
description = forms.CharField(required=False)
class Meta:
model = Images
fields = ('album_name', 'description',)
views.py is -
class RandomView(TemplateView):
template_name = 'photos/random.html'
def get(self, request, album_name):
images = Images.objects.filter(album_name=album_name)
context = {'album_name':album_name, 'images' : images}
return render(request, 'photos/random.html', context)
def post(self, request, album_name):
form = ImagesForm(request.POST)
if form.is_valid():
form.save(commit=False)
text = form.cleaned_data['description']
Images.album_name = album_name
form.save()
else:
return HttpResponse("Failed to save")
Templates is -
<h3>Album : {{album_name }}</h3>
{% for image in images %}
<img src="{{image.image.url}}" height="400" width="500">
<h4> {{image.image_name }}</h4>
<form method="POST" action=""> {% csrf_token %}
<span class = "badge">Description</span>
{% if image.description %}
<h4> {{image.description }} </h4>
{% else %}
<input type="text" value=" "/>
<button type="Submit">Submit</button>
{% endif %}
</form>
{% endfor %}
Where is your necessary name and id attributes for your input tag?
<input type="text" name="description" id="id_description"/>
Please try with {{ form.errors }} above "form" tag. And first of all check that what the errors arrive. Then Find the solution based on that error. Let me know if it is helpful or not.
I have a modelform that only works(saves input data to database) if none of the fields has choices. When i introduce choices, i don't get any errors and the form seems to be valid but nothing gets saved.
I have combed through the documentation and i am not returning anything useful.
I am convinced that i need to do more in my views to get the selected input choices or i need to add a few methods to the model class. Please point me in the right direction.
Here is my model:
class OpeningHours(models.Model):
'''
'''
class Meta:
verbose_name = 'Opening Hour'
verbose_name_plural = 'Opening Hours'
#######################################################
mytime = Bizhours()
################################################
id = models.AutoField(primary_key=True)
company =models.CharField(max_length=100, null=True, blank=True)
weekday = models.CharField(max_length=100, choices=mytime.getweekdays(), default='Monday', null=True)
fromHour = models.CharField(max_length=100, null=True, blank=True)
fromMinute = models.CharField(max_length=100, null=True, blank=True)
toHour = models.CharField(max_length=100, null=True, blank=True)
toMinute = models.CharField(max_length=100, null=True, blank=True)
'''
id = models.AutoField(primary_key=True)
company = models.ForeignKey(Company)
weekday = models.IntegerField(choices=mytime.getweekdays())
fromHour = models.TimeField(choices=mytime.gettime12())
fromMinute = models.TimeField(choices=mytime.getminutes())
toHour = models.TimeField(choices=mytime.gettime12())
toMinute = models.TimeField(choices=mytime.getminutes())
'''
def __str__(self):
return "%s %s (%s - %s)" % (self.company, self.weekday, self.fromHour, self.toHour)
here is my views
#login_required
def addprofile(request):
current_user = request.user
#OpeningHoursFormSet = modelformset_factory(OpeningHours, form=OpeningHoursForm,extra=1)
if request.session['entry_count'] > 1:
messages.success( request, 'You can only create two business profiles now' )
return HttpResponseRedirect( reverse('home') )
else:
if request.method == 'POST':
form = OpeningHoursForm(request.POST)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.company ="thiscompany"
model_instance.weekday = request.POST.get('weekday')
model_instance.save()
else:
print("problems saving edited form")
return HttpResponseRedirect('/bizprofile/success')
else:
form = OpeningHoursForm()
context = {'form': form}
return render_to_response('bizprofile/addprofile.html', context, context_instance=RequestContext(request))
here is the form
{% extends "bizprofile/bizprofilebase.html" %}
{% block content %}
{% if form.subject.errors %}
<ol>
{% for error in form.subject.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
{% if user.is_authenticated %}
<p>Welcome, {{ user.get_username }}. Thanks for logging in.</p>
<form method="post" action="">
{% csrf_token %}
<table>
{{form}}
</table>
<input type="submit" value="Submit Form"/>
</form>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
{% endblock %}
The problem lies in the fact that OP is using CharField for weekday data type, but the choices returned from a function are defined as integers. Since they are not compatible, the data could not be saved.
New to Django and Python and I need a little help with a foreign key drop down. Basically, I have a category model and a image model and I want users to be able to choose which category to put the image in. How do I create a drop down for the category in the image form? Are my views and html correct too? I have had a look online but I can't seem to do it myself. I keep getting errors.
Here are my models:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
def __unicode__(self):
return self.img_name
class Categories(models.Model):
cat_descr = models.CharField(max_length =120, blank=False)
def __unicode__(self):
return self.cat_descr
VIEWS:
#login_required
def upload_images(request):
context = RequestContext(request)
context_dict={}
if request.method == 'POST': # render the form, and throw it back.
# take the form data and process it!
form = UploadImagesForm(request.POST, request.FILES)
if form.is_valid():
print 'form is_valid'
upload_image = form.save(commit=False)
upload_image.img_user = request.user
if 'image' in request.FILES:
upload_image.image =request.FILES['image']
upload_image.save()
return render(request, 'rmb/upload.html', {'upload_image': form})
else:
print form.errors
# Not a HTTP POST, so we render our form using two ModelForm instances.
# These forms will be blank, ready for user input.
else:
form = UploadImagesForm()
context_dict = {'upload_image': form}
all_categories = Categories.objects.order_by('-id')
context_dict['all_categories'] = all_categories
print context_dict
return render_to_response('rmb/upload.html', context_dict, context)
FORMS:
class UploadImagesForm(forms.ModelForm):
#cat_list = ModelChoiceField(queryset=Categories.objects.all())
class Meta:
model = Images
fields=('image','img_name')
HTML:
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ upload_image.as_table }}
<input type="submit" name="submit" value="Upload" />
{% for categories in all_categories %}
<div> {{ categories.id }} </div>
{{ categories.cat_descr }}
<input type="submit" name="submit" value="Upload" />
{% endfor %}
</form>
{% endblock %}
You don't need to insert the HTML for the form manually, just use {{form}} in the template.
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
</form>
{% endblock %}
By default a ForeignKey will be a select field so you shouldn't need to do much else.
As an aside, give your models and fields more appropriate names. We know these are all image fields, because they are on the image and make sure, unless your model is a collection of things, you give it a singular name. Lastly, when using a Foreign Key and item gets an extra field of fieldname_id that is just the ID, whereas fieldname is the property that gives the related item as well.
So instead of:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
Use:
class Image(models.Model):
image = models.ImageField(upload_to='images', blank=False)
name = models.CharField(max_length=120, blank=True)
date = models.DateTimeField(default=now())
user = models.ForeignKey(User)
category = models.ForeignKey(Categories)