Authentication form is never valid in django - python

I have django app with authentication in it. I have forms.py file like this:
<form class="login_form" method="post" novalidate>
{% csrf_token %}
<div class="col-md-3 col-md-auto text-center">
<b>First Name:<b>
{{ form.first_name }}
{% if 'first_name' in error %}
<div style='color:red'>{{error.first_name.0.message}}</div>
{% endif %}
</div>
<div class="col-md-3 col-md-auto text-center">
<b>Last Name:<b>
{{ form.last_name }}
{% if 'last_name' in error %}
<div style='color:red'>{{error.last_name.0.message}}</div>
{% endif %}
</div>
<div class="col-md-3 col-md-auto text-center">
<b>username:<b>
{{ form.username }}
{% if 'username' in error %}
<div style='color:red'>{{error.username.0.message}}</div>
{% endif %}
</div>
<div class="col-md-3 col-md-auto text-center">
<b>Email:<b>
{{ form.email }}
{% if 'email' in error %}
<div style='color:red'>{{error.email.0.message}}</div>
{% endif %}
</div>
<div class="col-md-3 col-md-auto text-center">
<b>Password<b>
{{ form.password }}
{% if 'password' in error %}
<div style='color:red'>{{error.password.0.message}}</div>
{% endif %}
</div>
<div class="col-md-3 col-md-auto text-center">
<b>Confirm password:<b>
{{ form.confirm_password }}
{% if 'confirm_password' in error %}
<div style='color:red'>{{error.confirm_password.0.message}}</div>
{% endif %}
</div>
<button type="submit" class="submit_btn">Sign Up</button>
<span class="peder"><p>If you already have account<br>Login</p></span>
</form>
and forms.py:
class CustomerSignUpForm(UserCreationForm):
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
email = forms.EmailField(required=True)
username = forms.CharField(max_length=40)
password = forms.CharField(max_length=30)
confirm_password = forms.CharField(max_length=30)
class Meta(UserCreationForm.Meta):
model = User
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_customer = True
user.save()
customer = Customer.objects.create(user=user)
customer.first_name = self.cleaned_data.get('first_name')
customer.last_name = self.cleaned_data.get('last_name')
customer.username = self.cleaned_data.get('username')
customer.email = self.cleaned_data.get('email')
customer.password = self.cleaned_data.get('password')
customer.confirm_password = self.cleaned_data.get('confirm_password')
customer.save()
return user
My model looks like this:
class User(AbstractUser):
is_customer = models.BooleanField(default=False)
is_employee = models.BooleanField(default=False)
# first_name = models.CharField(max_length=100)
# last_name = models.CharField(max_length=100)
signup_confirmation = models.BooleanField(default=False)
class Customer(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE, primary_key = True)
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
username = models.CharField(max_length=40, blank=True)
email = models.EmailField(max_length=40, blank=True)
password = models.CharField(max_length=30, blank=True)
confirm_password = models.CharField(max_length=30, blank=True)
So when user clicks submit button on signup form it should save the form and send him an email.
view
class customer_register(CreateView):
model = User
form_class = CustomerSignUpForm
template_name = 'authentication/customer_register.html'
def form_valid(self, form):
user = form.save()
# current_site = get_current_site(request)
current_site = '127.0.0.1:8000'
subject = 'Please Activate Your Account'
message = render_to_string('authentication/email/activation_request.html', {
'user': user,
'domain': current_site,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user),
})
send_mail(
subject,
message,
'from#example.com',
['to#example.com'],
fail_silently=False,
)
return redirect('/')
But when I click submit button on my page nothing happens. In my terminal I do get [13/Sep/2022 08:18:39] "POST /accounts/customer_register/ HTTP/1.1" 200 9660 but user is not created and email is not sent. Could someone explain why this is happening and present the solution? Thanks:)

First of all.
We need to install few dependencies.
1. pip install django-crispy-forms
2. add 'crispy_forms', to your INSTALLED_APPS in your projects settings.py
3. add CRISPY_TEMPLATE_PACK = 'bootstrap4' to your settings.py
authentication/customer_register.html
We simplify the form through crispy forms that gives us a bootstrap 4 result.
{% load crispy_forms_tags %}
<form class="login_form" method="post" novalidate>
{% csrf_token %}
{% crispy form %}
<button type="submit" class="btn btn-success mt-3 float-end">Submit</button>
</form>
forms.py
We create a modelForm taking the model User and its fields as Meta:
from django import forms
class CustomerSignUpForm(forms.ModelForm):
class Meta:
model = User
fields = ['first_name', 'last_name', 'email',
'username', 'password', 'confirm_password']
views.py
#The generic view CreateView Polymorphism already benefits of the post method that will save the user
from django.views.generic import CreateView
from.forms import CustomerSignUpForm
class CustomerRegisterCreateView(CreateView):
model = User
form_class = CustomerSignUpForm
template_name = 'authentication/customer_register.html'
urls.py
We create a path so we can link it to a URL with not forgetting the .as_view() as it is a class based view ( function based views do not require this )
from .views import (
CustomerRegisterCreateView,
)
urlpatterns = [
path('customer-register/', CustomerRegisterCreateView.as_view(), name="customer-register"),
]
models.py
Once a user is created, we use a signal that will trigger on user creation.
from django.db.models.signals import post_save
def User_receiver(sender, instance, created, *args, **kwargs):
if created:
print('we went thought the User signal !')
subject = "Welcome"
email_template_name = "authentication/Register-email.txt"
user = User.objects.get(username=instance.username)
c = {
'username': user,
}
email = instance.email
email_1 = render_to_string(email_template_name, c)
send_mail(subject, email_1, 'your_email',
[email], fail_silently=False)
'''
post_save.connect(User_receiver, sender=User)

Related

how to correctly saving a profile instance in django

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.

django User matching query does not exist

i'm trying to signup with an otp for verification by sending email to the user mail, but getting this error, it's might be problem with signup.html, from where trying to get specific user otp to validate the data, if is there any better solution do this with django would be appreciate,
models.py
class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
is_buyer = models.BooleanField(default=False)
is_vendor = models.BooleanField(default=False)
objects = CustomUserManager()
def __str__(self):
return self.email
class UserOTP(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE)
time_st = models.DateTimeField(auto_now = True)
otp = models.SmallIntegerField()
class Vendor(models.Model):
user = models.OneToOneField(User, related_name='vendor', on_delete=models.CASCADE)
business_name = models.CharField(max_length=50)
def __str__(self):
return self.user.email
forms.py
class VendorSignUpForm(UserCreationForm):
business_name = forms.CharField(required=True)
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
class Meta:
model = User
fields = ('business_name', 'email', 'password1', 'password2', )
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_vendor = True
user.save()
customer = Vendor.objects.create(user=user)
customer.business_name=self.cleaned_data.get('business_name')
customer.save()
return user
views.py
def signup(request):
if request.method == 'POST':
get_otp = request.POST.get('otp')
print(get_otp)
if get_otp:
get_user = request.POST.get('user')
user = User.objects.get(email=get_user)
if int(get_otp) == UserOTP.objects.filter(user = user).last().otp:
user.is_active = True
user.save()
messages.success(request, f'Account is Created For {user.email}')
return redirect('login')
else:
messages.warning(request, f'You Entered a Wrong OTP')
return render(request, 'registration/signup.html', {'otp': True, 'user': user})
form = VendorSignUpForm(request.POST)
if form.is_valid():
form.save()
email = form.cleaned_data.get('email')
user = User.objects.get(email=email)
print(user)
user.email = email
user.is_active = False
user.save()
usr_otp = random.randint(100000, 999999)
UserOTP.objects.create(user=user, otp = usr_otp)
mess = f"Hello {user.email},\nYour OTP is {usr_otp}\nThanks!"
send_mail( 'Welcome to Costume Base - Verify Your Email',
mess , settings.DEFAULT_FROM_EMAIL, [user.email],
fail_silently = False)
return render(request, 'registration/signup.html', {'otp': True, user: user})
else:
form = VendorSignUpForm()
return render(request, 'registration/signup.html', {'form': form})
signup.html
{% block content %}
<div>
<h2>Signup</h2>
</div>
<div>
{% if otp %}
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom display">
<h3 class="float-left">Verify Your Email</h3>
</legend>
** is this input value showing the error? **
<input type="hidden" value="{{user.email}}" name = 'user' >
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1">OTP</span>
</div>
<input type="text" class="form-control" placeholder="Enter Your OTP" aria-label="OTP"
aria-describedby="basic-addon1" name = 'otp'>
</div>
</fieldset>
<div class="form-grounp">
<button class="btn mainbtn" type="submit">Verify</button>
<small class="float-right text-muted"><i><a href="#" class="text-dark"
onclick="ReSendOTP('{{user.email}}', 'resendOTPmess')"><i id="resendOTPmess">Resend</i> OTP</a></small>
</div>
</form>
{% else %}
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Register</button>
</form>
</div>
{% endif %}
{% endblock content %}
From the Django documentation:
This exception is raised by the ORM when an expected object is not found. For example, QuerySet.get() will raise it when no object is found for the given lookups.
That's probably due to one of your Model.get() methods, e.g:
user = User.objects.get(email=get_user)
Try to handle it with a try/catch or use a .filter() method instead of .get()

How can I add a comment to user on an app that has a relationship many-to-many with another model that has a relationship with other?

I get a huge stuck. now I have many of models which has a relationship between them at many apps I 'll explain that as following:
- The first model is (UserProfile) that has (one to one) relation with (User) model
also, I have (UserAsking) that has relation (ForeignKey) with (UserProfile) and last part is (Comment) That has (Many to many) relations with (UserAsking). in this case, I want to make a comment and this comment that has a relationship with UserAsking model. I'm in trouble, how can I do that?
I find that (many-to-many) is different from any another relationship and I can't get the instance as an argument in (Comment) model
if anyone can give me any help?
thank you in advance
account/models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
CHOICE = [('male', 'male'), ('female', 'female')]
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
overview = models.TextField(editable=True, blank=True, default='You have no an Overview yet')
city = models.CharField(max_length=20, blank=False)
phone = models.IntegerField(default=0, blank=True)
sex = models.CharField(max_length=10, default='male', choices=CHOICE)
skill = models.CharField(max_length=100, default='You have no skills yet')
logo = models.ImageField(upload_to='images/', default='images/default-logo.jpg', blank=True)
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(receiver=create_profile, sender=User)
community/models.py
from django.db import models
from account.models import UserProfile
from django.db.models.signals import post_save
CHOICE = [('Technology', 'Technology'), ('Computer Science', 'Computer Science'),
('Lawyer', 'Lawyer'), ('Trading', 'Trading'),
('Engineering', 'Engineering'), ('Life Dialy', 'Life Dialy')
]
class UserAsking(models.Model):
userprofile = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
title = models.CharField(max_length=100, blank=False, help_text='Be specific and imagine you’re asking a question to another person')
question = models.TextField(max_length=500, blank=False, help_text='Include all the information someone would need to answer your question')
field = models.CharField(max_length=20, choices=CHOICE, default='Technology', help_text='Add the field to describe what your question is about')
def __str__(self):
return self.title
class Comment(models.Model):
userasking = models.ManyToManyField(UserAsking)
comment = models.TextField(max_length=500, blank=True)
community/views.py
from django.shortcuts import render, redirect, get_list_or_404
from .forms import UserAskingForm, CommentForm
from .models import UserAsking
from django.contrib.auth.decorators import login_required
#login_required
def user_asking(request):
form = UserAskingForm
if request.method == 'POST':
form = UserAskingForm(request.POST, instance=request.user.userprofile)
if form.is_valid():
asking = form.save(commit=False)
asking.title = form.cleaned_data['title']
asking.question = form.cleaned_data['question']
asking.field = form.cleaned_data['field']
asking = UserAsking.objects.create(userprofile=request.user.userprofile,
title=asking.title,
question=asking.question,
field=asking.field)
asking.save()
return redirect('community:user_questions')
else:
form = UserAskingForm()
return render(request, 'community/asking_question.html', {'form': form})
return render(request, 'community/asking_question.html', {'form': form})
#login_required
def user_questions(request):
all_objects = UserAsking.objects.all().order_by('-title')
all_objects = get_list_or_404(all_objects)
return render(request, 'community/user_questions.html', {'all_objects': all_objects})
def question_view(request, user_id):
my_question = UserAsking.objects.get(pk=user_id)
comment_form = CommentForm
#x = request.user.userprofile.userasking_set
if request.method == 'GET':
comment_form = comment_form(request.GET)
if comment_form.is_valid():
comments = comment_form.save(commit=False)
comments.comment = comment_form.cleaned_data['comment']
# you have to edit on userasking instance
#comments = Comment.objects.create(userasking=request.user.userprofile.userasking_set, comment=comments)
comments.save()
#return render(request, 'community/question_view.html', {'x': x})
return render(request, 'community/question_view.html', {'my_question': my_question,
'comment': comment_form})
community/forms.py
from django import forms
from .models import UserAsking, Comment
class UserAskingForm(forms.ModelForm):
title = forms.CharField(required=True,
widget=forms.TextInput(attrs={'placeholder': 'Type Your Title...',
'class': 'form-control',
'data-placement': 'top',
'title': 'type your title',
'data-tooltip': 'tooltip'
}),
help_text='Be specific and imagine you’re asking a question to another person')
question = forms.CharField(required=True,
widget=forms.Textarea(attrs={'placeholder': 'Type Your Details Of Your Question...',
'class': 'form-control',
'data-placement': 'top',
'title': 'type your question simply',
'data-tooltip': 'tooltip'
}),
help_text='Include all the information someone would need to answer your question')
class Meta:
model = UserAsking
fields = '__all__'
exclude = ['userprofile']
class CommentForm(forms.ModelForm):
comment = forms.CharField(max_length=500, required=False, widget=forms.Textarea(attrs={'placeholder': 'Type your comment simply',
'class': 'form-control'}))
class Meta:
model = Comment
fields = ['comment']
community/question_view.html
{% extends 'base.html' %}
{% block title %} This Question Belong To User: {{ request.user }} {% endblock %}
{% block body %}
<!-- Full Question View -->
<div class="my_question">
<div class="container">
<div class="answer-question">
<div class="row">
<div class="col-md-6 col-xs-12">
<div class="title">
<h3 class="text-primary">{{ my_question.title }}</h3>
<span class="clock">1 hour ago</span>
</div>
<div class="question">
<p class="">{{ my_question.question }}</p>
</div>
<div class="field">
<span>{{ my_question.field }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Options e.g 'Edit, Comment, Delete etc...' -->
<div class="options">
<div class="container">
<div class="col-sm-12">
<a data-showin=".my-form" class="showin">Comment</a> |
Edit |
Delete
<span>
Like |
Unlike
</span>
</div>
<hr>
<!-- Comment Text -->
<div class="user-answer">
<div class="row">
<div class="col-xs-12">
{% for field in comment %}
<p>(medo) - sub comment</p>
<p>1 hour ago</p>
{% endfor %}
</div>
</div>
</div>
<!-- Comment Field -->
{% include 'community/comment_form.html' %}
{{ x }}
</div>
</div>
{% endblock %}
community/comment_form.html
<form method="get" action="" class="hide my-form">
{% csrf_token %}
<div class="row">
{% for field in comment %}
<div class="col-sm-10">
<div class="form-group">
{{ field }}
</div>
</div>
<div class="col-sm-1">
<button type="submit" class="btn btn-primary btn-lg">Add Comment</button>
</div>
{% endfor %}
</div>
</form>
community/urls.py
from . import views
from django.urls import path
app_name = 'community'
urlpatterns = [
path('', views.user_questions, name='user_questions'),
path('ask-question/', views.user_asking, name='user_asking'),
path('ask-question/question-view/<int:user_id>/', views.question_view, name='question_view'),
]

TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'

I've created a custom user model in Django and everything went fine, but when I try to create a new user it gives me an error.
Here are my files :
models.py, where I created the user table:
class UserModelManager(BaseUserManager):
def create_user(self, email, password, pseudo):
user = self.model()
user.name = name
user.email = self.normalize_email(email=email)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password):
'''
Used for: python manage.py createsuperuser
'''
user = self.model()
user.name = 'admin-yeah'
user.email = self.normalize_email(email=email)
user.set_password(password)
user.is_staff = True
user.is_superuser = True
user.save()
return user
class UserModel(AbstractBaseUser, PermissionsMixin):
## Personnal fields.
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=16)
## [...]
## Django manage fields.
date_joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELD = ['email', 'name']
objects = UserModelManager()
def __str__(self):
return self.email
def get_short_name(self):
return self.name[:2].upper()
def get_full_name(self):
return self.name
forms.py, where I created the signup form:
class SignUpForm(UserCreationForm):
email = forms.CharField(required=True, help_text='البريد الإلكترونى الخاص بك - يجب ان يكون حقيقى (يستخدم لتسجيل الدخول) ')
name = forms.CharField(required=True, help_text='إسمك الحقيقى - سيظهر كأسم البائع')
password1 = forms.CharField(widget=forms.PasswordInput,
help_text='كلمة المرور - حاول ان تكون سهلة التذكر بالنسبة لك')
password2 = forms.CharField(widget=forms.PasswordInput,
help_text='تأكيد كلمة المرور - إكتب نفس كلمة المرور السابقة مرة أخرى')
class Meta:
model = UserModel
fields = ('email','name', 'password1', 'password2', )
labels = {
'name': 'إسمك الحقيقى - سيظهر كأسم البائع',
'email': 'البربد الإلكترونى Email',
'password1': 'كلمة المرور',
'password2': 'تأكيد كلمة المرور'
}
views.py with a view that renders the signup template:
def signup(request):
if request.method == 'POST':
signup_form = SignUpForm(request.POST)
if signup_form.is_valid():
signup_form.save()
username = signup_form.cleaned_data.get('username')
raw_password = signup_form.cleaned_data.get('password1')
user = authenticate(username=username, password=raw_password)
login(request, user)
return redirect('signup_confirm')
else:
signup_form = SignUpForm()
context = {
'signup_form': signup_form,
}
return render(request, 'fostania_web_app/signup.html', context)
Finally, signup.html, the template itself where the form is being used:
{% extends 'fostania_web_app/base.html' %}
{% block content %}
{% load static %}
<br>
<br>
<div align="center">
<div class="card border-primary mb-3" style="max-width: 40rem;" align="center">
<div class="card-header">إنشاء حساب جديد</div>
<div class="card-body text-dark">
<h5 class="card-title">
<img src="{% static 'img/add_user_big.png' %}">
</h5>
<p class="card-text"> {% if user.is_authenticated %}
<div align="center">
لقم قمت بتسجيل الدخول بالفعل <br>
يمكنك <a href = '{% url 'logout' %}'>
<button class="btn btn-danger" >تسجيل الخروج</button> </a> إذا كنت ترغب الدخول على حساب اخر
</div>
{% else %}
<form method="post" align="right">
{% csrf_token %}
{% for field in signup_form %}
<p>
{{ field.label_tag }}<br>
{{ field }}<br>
<font color="gray">{{ field.help_text }}</font>
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
{% endfor %}
<br>
<button type="submit" class="btn btn-success">تسجيــل</button><br>
بالضغط على تسجيل انت توافق على شروط الإستخدام الخاصة بموقعنا !
</form>
</div>
{% endif %}
</p>
</div>
</div>
{% endblock %}
The error I'm getting is:
ImproperlyConfigured at /signup/
TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'
I suspect that the problem is actually in your urls.py. If you point your url to the wrong view (maybe a CBV that you have associated to the /signup/ path), that could cause this error.

Django: Mark as Read "Notifications"

I'm working on a school project. Right now any user can ask a question.
In order to notify all the users when any users asks a question I've created a new app & notifying them through the simple 'view' whenever a question is asked. But it's just plain notifications yet.
How can I mark them read once a user opens the Notification tab? Just like on social networks!
I suggest you to use ContentType to make a dynamic notifications fo any models. This snippet below is an example how to implement the notification system;
1. in your models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.utils.translation import ugettext_lazy as _
class ContentTypeToGetModel(object):
"""
requires fields:
- content_type: FK(ContentType)
- object_id: PositiveIntegerField()
"""
def get_related_object(self):
"""
return the related object of content_type.
eg: <Question: Holisticly grow synergistic best practices>
"""
# This should return an error: MultipleObjectsReturned
# return self.content_type.get_object_for_this_type()
# So, i handle it with this one:
model_class = self.content_type.model_class()
return model_class.objects.get(id=self.object_id)
#property
def _model_name(self):
"""
return lowercase of model name.
eg: `question`, `answer`
"""
return self.get_related_object()._meta.model_name
class Notification(models.Model, ContentTypeToGetModel):
# sender = models.ForeignKey(
# User, related_name='notification_sender')
receiver = models.ForeignKey(
User, related_name='notification_receiver')
content_type = models.ForeignKey(
ContentType, related_name='notifications', on_delete=models.CASCADE)
object_id = models.PositiveIntegerField(_('Object id'))
content_object = GenericForeignKey('content_type', 'object_id')
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
STATUS_CHOICES = (
('reply', _('a reply')),
('comment', _('a comment')),
('message', _('a message'))
)
status = models.CharField(
_('Status'), max_length=20,
choices=STATUS_CHOICES, default='comment')
is_read = models.BooleanField(
_('Is read?'), default=False)
def __str__(self):
title = _('%(receiver)s have a %(status)s in the %(model)s:%(id)s')
return title % {'receiver': self.receiver.username, 'status': self.status,
'model': self._model_name, 'id': self.object_id}
class Meta:
verbose_name_plural = _('notifications')
ordering = ['-created']
2. in your views.py
from django.views.generic import (ListView, DetailView)
from yourapp.models import Notification
class NotificationListView(ListView):
model = Notification
context_object_name = 'notifications'
paginate_by = 10
template_name = 'yourapp/notifications.html'
def get_queryset(self):
notifications = self.model.objects.filter(receiver=self.request.user)
# mark as reads if `user` is visit on this page.
notifications.update(is_read=True)
return notifications
3. in your yourapp/notifications.html
{% extends "base.html" %}
{% for notif in notifications %}
{{ notif }}
{# for specific is like below #}
{# `specific_model_name` eg: `comment`, `message`, `post` #}
{% if notif._model_name == 'specific_model_name' %}
{# do_stuff #}
{% endif %}
{% endfor %}
So, when I creat that notifications?
eg: when the other user send a comment to receiver on this post.
from django.contrib.contenttypes.models import ContentType
def send_a_comment(request):
if request.method == 'POST':
form = SendCommentForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
#instance.sender = request.user
...
instance.save()
receiver = User.objects.filter(email=instance.email).first()
content_type = ContentType.objects.get(model='comment')
notif = Notification.objects.create(
receiver=receiver,
#sender=request.user,
content_type=content_type,
object_id=instance.id,
status='comment'
)
notif.save()
How about menu? Like this stackoverflow, facebook, instagram, or else?
you can handle it with templatetags, eg:
# yourapp/templatetags/notification_tags.py
from django import template
from yourapp.models import Notification
register = template.Library()
#register.filter
def has_unread_notif(user):
notifications = Notification.objects.filter(receiver=user, is_read=False)
if notifications.exists():
return True
return False
and the navs.html menu:
{% load notification_tags %}
{% if request.user.is_authenticated %}
<ul class="authenticated-menu">
<li>
<a href="/notifications/">
{% if request.user|has_unread_notif %}
<i class="globe red active icon"></i>
{% else %}
<i class="globe icon"></i>
{% endif %}
</a>
</li>
</ul>
{% endif %}
First you need ManyToManyField for user module who is readed a post.
profile.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
subscribed = models.ManyToManyField(User, related_name='subscribed', blank=True)
readed = models.ManyToManyField("blogs.BlogPost", related_name='readed', blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user.username)
class Meta:
ordering = ("-created",)
If you have a blog model like this:
class BlogPost(models.Model):
author = models.ForeignKey(Profile, on_delete=models.CASCADE)
post_title = models.CharField("Post Title", max_length=150, unique=True)
post_content = models.TextField("Content")
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.post_title
class Meta:
ordering = ("-created",)
You need to create a button function in blog posts view.
def mark_as_read_button(request):
if request.method == "POST":
my_profile = Profile.objects.get(user=request.user)
post = request.POST.get("post_pk")
obj = BlogPost.objects.get(pk=post)
if obj in my_profile.readed.all():
my_profile.readed.remove(obj)
else:
my_profile.readed.add(obj)
return redirect(request.META.get("HTTP_REFERER"))
return redirect("blogs:subscribed-blogs")
Then you can use is in the template file like this:
{% extends 'base.html' %}
{% block title %}Subscribed Blog Posts{% endblock %}
{% block content %}
{% for post in posts %}
<h5 class="card-title">{{ post.post_title }}</h5>
{% if post in readed %}
<form action="{% url "blogs:mark_as_read" %}" method="POST">
{% csrf_token %}
<input type="hidden" name="post_pk" value={{ post.pk }}>
<button type="submit" class="btn btn-danger btn-sm">Mark as unread</button>
</form>
{% else %}
<form action="{% url "blogs:mark_as_read" %}" method="POST">
{% csrf_token %}
<input type="hidden" name="post_pk" value={{ post.pk }}>
<button type="submit" class="btn btn-success btn-sm">Mark as read</button>
</form>
{% endif %}
<p class="card-text">{{ post.created }}</p>
<p class="card-body">{{ post.post_content }}</p>
<hr>
{% endfor %}
{% endblock %}
Good coding.

Categories