How can I create "profile" urls without explicit passing arguments in django? - python

I am kinda new at django. For example let's say I'd like to create a BrAnD nEw social network as a pet project. I want my urls to look something like example.com/my-profile but not as example.com/profile/slug_or_pk. I have a user and profile models in different apps for now. Is it actually possible somehow to hide the slug?
Models.py
class Profile(models.Model):
user = models.OneToOneField(User, verbose_name='User', on_delete=models.CASCADE)
slug = models.SlugField(null=True)
about = models.TextField('About', null=True, blank=True)
avatar = models.ImageField("Avatar", upload_to=f"profile/", blank=True, null=True)
cover = models.ImageField("Cover", upload_to="profile/", blank=True, null=True)
def __str__(self):
return self.user
def get_absolute_url(self):
return reverse('profile_detail', kwargs={'slug': self.slug})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.user.username)
return super().save(*args, **kwargs)
class Meta:
verbose_name = "Profile"
verbose_name_plural = "Profiles"
views.py
class ProfileDetail(DetailView):
model = Profile
context_object_name = 'profile'
template_name = 'user_detail.html'
urls.py
urlpatterns = [
path('<slug:slug>/', ProfileDetail.as_view(), name='profile_detail'),
]
And the root URLconf:
urlpatterns = [
path('', IndexView.as_view(), name='index'),
path('register/', RegisterUserView.as_view(), name='register'),
path('login/', LoginUserView.as_view(), name='login'),
path('logout/', LogoutUserView.as_view(), name='logout'),
path('users/', include('users.urls')),
path('profile/', include('profiles.urls')),
path('posts/', include('posts.urls')),
]
I've looked over tutorials, articles and etc.but I didn't manage to find something really useful. Maybe I am wrong somewhere at the fundamentals. Please help!

The only thing you need is Django's TemplateView with LoginRequiredMixin (this will make sure, that User is authenticated):
from django.contrib.auth.mixins import LoginRequiredMixin
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = "user_profile.html"
urls:
urlpatterns = [
path('', ProfileView.as_view(), name='user_profile'),
path('<slug:slug>/', ProfileDetail.as_view(), name='profile_detail'),
]
In the template just refer to request.user as the User model instance and you will always get data related to User that is actually seeing the site. In example:
<table>
<tr>
<td>
First name
</td>
<td>
{{ request.user.first_name }}
</td>
</tr>
<tr>
<td>
Last name
</td>
<td>
{{ request.user.last_name }}
</td>
</tr>
</table>

Related

Why my URL doesn't activate view in django?

Here is the thing i'm trying to do:
There are few tv channels as image links and they will have some information inside channels.
I managed to create tv channels as a list, they look like a link but they don't work like link.
I've created slug area for each channel that takes from it's own name and generates slug auto. And with get_absolute_url i take it's url in the below code you'll see;
This is my model :
class Channels(models.Model):
name = models.CharField(max_length=50, null=False, blank=False, verbose_name="Tv Kanalı")
logo = models.ImageField(upload_to="channels/images/", verbose_name="Tv Logosu", blank=False)
slug = models.SlugField(null=True, unique=True, editable=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Channels, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('channel-list:campaigns', kwargs={'page_slug': self.slug})
This is my main urls:
urlpatterns = [
path('admin/', admin.site.urls),
url(r'', include(('tvekstra.apps.core.urls', 'home'), namespace='home')),
url(r'^user/', include(('tvekstra.apps.user.urls', 'login'), namespace='user')),
url(r'^channels/', include(('tvekstra.apps.tvchannels.urls', 'main'), namespace="channel-list")),
This is channels urls:
urlpatterns = [
url(r'', views.channel_list, name='channels'),
url(r'^(?P<page_slug>[-\w]+)/$', views.campaign_list, name='campaigns'),
]
This is my views:
def channel_list(request):
channels = Channels.objects.all()
return render(request, 'channel.list.html', {'channels': channels})
def campaign_list(request, page_slug):
channel = get_object_or_404(Channels, slug=page_slug)
return render(request, 'campaign.list.html', {'channel': channel})
And this is my template:
{% for channel in channels %}
<div class="col-3 text-center channels">
<a href="{{ channel.get_absolute_url }}">
<img src="{{ channel.get_image }}" alt="Channel Image" class="ch-img">
{{ channel.name }}
</a>
</div>
{% endfor %}
As you can see, a href is channel's getabsoluteurl method. It creates the html but it doesn't go forward page.
I think view is not working for some reason, requesting aid...
url(r'', matches '', but it also matches /channels/fox/ and /something-else/.
You can fix it by adding ^$ to the regex.
url(r'^$', views.channel_list, name='channels'),
In Django 2.0+, it would be better to use re_path, because url is deprecated in Django 3.1:
re_path(r'^$', views.channel_list, name='channels'),
And it would be simpler to use path:
path('', views.channel_list, name='channels'),

Django: How to filter and display posts from categories on a page

I'm new to Django and have build a simple application that includes posts. I want to display posts that are associated with certain categories on one page. I did quite a bit of research online but can't seem to make it work. I think the problem is in my views.py
I guess there's something wrong with the get_queryset function in CategoryListView.
'''
models.py
'''
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(
'Category',
on_delete=models.SET_NULL,
null=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=150, unique=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('post-category', kwargs={'pk': self.pk})
'''
views.py
'''
from django.views.generic import ListView
from .models import Post, Category
class CategoryListView(ListView):
model = Post
template_name = 'posts/post_category.html'
def get_queryset(self):
category = get_object_or_404(Category, id=self.kwargs.get('category__name'))
return Posts.objects.filter(category_name=category)
'''
urls.py
'''
from .views import CategoryListView
urlpatterns = [
# urlpatterns for other pages left out for better readability
CategoryListView.as_view(), name='post-category')
]
The code gives me a 404 and the message that no Category matches my query.
The get_queryset [Django-doc] function should return something like:
class CategoryListView(ListView):
# ...
def get_queryset(self):
return Post.objects.filter(category_id=self.kwargs.get('pk'))
Furthermore in your url, you will need to use a pattern with a pk parameter, like:
urlpatterns = [
path('category/<int:pk>', CategoryListView.as_view(), name='post-category')
]
That being said, if you want to display information about the category, it might make more sense to make it a DetailView [Django-doc], and then query the relation in reverse, like:
class CategoryDetailView(DetailView):
model = Category
context_object_name = 'category'
template_name = 'posts/post_category.html'
and in the template, you can render data with:
Category name: {{ category.name }}
{% for post in category.post_set.all %}
post: {{ post.title }}
{% endfor %}
This works, but how dow I reverse the order of it to always show the latest post first?
Category name: {{ category.name }}
{% for post in category.post_set.all %}
post: {{ post.title }}
{% endfor %}

Generic detail view ProfileView must be called with either an object pk or a slug

I'm new to Django 2.0 and i'm getting this error when visiting my profile page view. It's working with urls like path('users/<int:id>') but i wanted to urls be like path('<username>'). Not sure what exactly is the problem. I hope you can help.
#views.py
class ProfileView(views.LoginRequiredMixin, generic.DetailView):
model = models.User
template_name = 'accounts/profile.html'
#urls.py
urlpatterns = [
path('', HomePageView.as_view(), name='home'),
path('signup', SignUpView.as_view(), name='signup'),
path('login', LoginView.as_view(), name='login'),
path('logout', logout_view, name='logout'),
path('<username>', ProfileView.as_view(), name='profile')
]
#base.html
<ul class="dropdown-menu">
<li>View Profile</li>
<li>Edit Profile</li>
</ul>
You need to tell your view to use username as the lookup field. You could either do this by defining slug_field and slug_url_kwarg on the model, or by overriding get_object. For example:
class ProfileView(views.LoginRequiredMixin, generic.DetailView):
model = models.User
template_name = 'accounts/profile.html'
slug_field = 'username'
slug_url_kwarg = 'username'
The first of these determines what field to use in the model lookup; the second determines what variable to use from the URL pattern.
Why you don't simply change your path to:
url('(?P<username>[\w]+)', ProfileView.as_view(), name='profile')
And then in your html do this:
{% url 'accounts:profile' username=user.username %}
Another way would be to do this:
url('accounts/profile', ProfileView.as_view(), name='profile')
And in your profile template use request.user to access you user data
EDIT:
Try to override the get_object method as explained here
def get_object(self):
return get_object_or_404(User, pk=request.session['user_id'])

Iterating over a model in Django template not rendering anything

Problem:
I am trying to simply iterate over my Category model and display a list of categories. I've successfully done so with posts, but for some reason I can't get it to work with my categories model (It doesn't even make it passed the {% if categories %} statement). Thus, the <h2>Categories:</h2> and below is not rendered at all.
I currently have two categories in my database ('Fitness' and 'Nutrition'). They are in my admin, and they also show up when I query through the command line. I can also successfully link to them from my 'post_detail' page (and display all posts within a category). However, I can't seem to iterate over the model to display all categories as a list...
Code:
post_list.html
<div class="container">
<h2>Categories:</h2>
{% if categories %}
<h2>Categories</h2>
<ul>
{% for category in categories %}
<li>{{ category.name }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
models.py
from django.db import models
from django.utils import timezone
from django.template.defaultfilters import slugify
class Category(models.Model):
name = models.CharField(max_length=255, blank=False, default='')
slug = models.SlugField(max_length=100, default='', unique=True)
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
ordering = ['name']
def __str__(self):
return self.name
def __unicode__(self):
return self.name
views.py
from .models import Post, Category
from .forms import PostForm
from django.shortcuts import redirect
from django.db.models import Count
def category_detail( request, slug ):
category = get_object_or_404( Category, slug= slug )
context = {
'category': category,
'posts': category.post_set.all()
}
return render( request,'blog/category_detail.html', context )
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
context = {
'categories': getSortedCategories()
}
return render(request, 'blog/post_list.html', {'posts': posts})
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.post_list, name='post_list'),
url(r'^(?P<slug>[-\w]+)/$', views.category_detail, name='category_detail'),
url(r'^post/(?P<pk>\d+)-(?P<slug>[-\w]+)/$', views.post_detail, name='post_detail'),
url(r'^post/new/$', views.post_new, name='post_new'),
url(r'^post/(?P<pk>\d+)/edit/$', views.post_edit, name='post_edit'),
]
You do not transmit categories in the template context, you need to rewrite return expression like this:
return render(request, 'blog/post_list.html', {'posts': posts, 'categories': getSortedCategories()})

Why is my Django UpdateView not seeing my model data (specifically the id/pk)?

I'm working on a Django Project in which I have a model (Order) which holds various orders received from customers. I'm trying to update one of the Order fields (status) via id using UpdateView (CancelOrder). I've written the UpdateView and modified the urls.py, but when I try to update the status of an order via url I get the error: No order found matching the query, despite having several orders stored. My question is, why is the UpdateView not seeing my model data? Have I missed something?
Views.py
from django.shortcuts import render, redirect, HttpResponse
from django.core.exceptions import *
from django.views.generic.edit import UpdateView
from menu.models import Item, Order, Customer
from menu.forms import OrderForm, CustForm, UpdateForm
from .serializers import ItemSerializer
from rest_framework import generics
class CancelOrder(UpdateView):
model = Order
field = ('status',)
urls.py
from django.conf.urls import url, patterns, include
from . import views
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = patterns('',
url(r'^$', views.CreateOrder, name='CreateOrder'),
url(r'^vieworder/', views.ViewOrder, name='ViewOrder'),
url(r'^confirmation/', views.ConfirmOrder, name='ConfirmOrder'),
url(r'^api/$', views.ItemList.as_view()),
url(r'^api/(?P<pk>[0-9]+)/$', views.ItemDetail.as_view()),
url(r'^cancel/(?P<pk>[0-9]+)/$', views.CancelOrder.as_view(), name='CancelOrder'),
)
urlpatterns = format_suffix_patterns(urlpatterns)
Model
class Order(models.Model):
order_date = models.DateField(default=timezone.now)
delivery_date = models.DateField(default=timezone.now)
status = models.CharField(max_length=10, default='open')
ordered_by = models.CharField(max_length=50, null=True)
#user input
name = models.CharField(max_length=50)
email = models.EmailField()
phone = models.CharField(max_length=50)
shipping_info = models.CharField(max_length=50, null=True)
billing_info = models.CharField(max_length=50, null=True)
notes = models.CharField(max_length=150)
total = models.DecimalField(default=0, max_digits=5, decimal_places=2)
surcharge = models.DecimalField(default=0, max_digits=5,decimal_places=2)
def __str__(self):
return self.name
Looking at the comments and doing a bit more research I realized that I didn't fully understand what I was trying to do; most importantly I didn't have a template. I've fixed the issue as follows:
views.py
class CancelOrder(UpdateView):
model = Order
fields = ('status',)
template_name = 'menu/order_form.html'
def get_success_url(self):
return reverse('confirm')
template. I've fixed the issue as follows:
urls.py
urlpatterns = patterns('',
...
url(r'^updateorder/(?P<pk>[0-9]+)/$', views.CancelOrder.as_view(), name='cancel')
)
urlpatterns = format_suffix_patterns(urlpatterns)
template
{% extends "menu/header.html" %}
{% block content %}
<form method="POST" action="">{% csrf_token %}
<div id='helper'>{% include 'menu/includes/helper.html' %}<div>
<input id="submit" type="submit" value="Cancel Order">
</form>
<p>*Type "cancelled" to cancel your order</p>
{% endblock %}
helper
{% for field in form %}
<div>
<div><span>{{field.errors}}</div></span>
<label>{{field.label_tag}}</label>
<div>{{field}}</div>
<div>
{% endfor %}
This solves the issue, thanks to everyone in the comments.

Categories

Resources