How to show different username in template body if i want to send email via EmailMultiAlternatives with Django. I have several email receivers.
Here is my code:
models.py:
class User(AbstractUser):
category = models.ForeignKey('news.Category', on_delete=models.CASCADE,
related_name='subscribers', null=True)
class Category(models.Model):
category = models.CharField(unique=True, max_length=255)
class Post(models.Model):
NEWS = 'NEWS'
POST = 'POST'
type_choice = [(NEWS, 'Nowost'), (POST, 'Post')]
author = models.ForeignKey('accounts.Author', on_delete=models.CASCADE)
date_of_creation = models.DateTimeField(auto_now_add=True)
text_content = models.TextField()
rating = models.IntegerField(default=0)
header = models.CharField(max_length=255)
category = models.ManyToManyField(Category, through='PostCategory')
type_of_content = models.CharField(max_length=4, choices=type_choice, default=NEWS)
signals.py
#receiver(m2m_changed, sender=Post.category.through)
def notify_subscribers(sender, instance, action,**kwargs):
if action == 'post_add':
email_of_subscribers = list(instance.category.all().values_list('subscribers__email', flat=True))
html_content = render_to_string(
r'mails_forms/post_created.html',
{'single_news': instance
}
)
msg = EmailMultiAlternatives(
subject=instance.header,
body=instance.text_content,
to=email_of_subscribers
)
msg.attach_alternative(html_content, 'text/html')
msg.send()
template mails_forms/post_created.html
Hello, {{ username }}. New post in your favorite category!
{{ single_news.text_content|truncatechars:50 }}
Open post
You can iterate over subscribers. And instead of values_list('subscribers__email', flat=True) use values('subscribers__email', 'subscribers__username')
#receiver(m2m_changed, sender=Post.category.through)
def notify_subscribers(sender, instance, action,**kwargs):
if action == 'post_add':
subscribers = instance.category.values(
'subscribers__email', 'subscribers__username'
)
for subscriber in subscribers:
html_content = render_to_string(
'mails_forms/post_created.html',
{
'single_news': instance,
'username': subscriber.get("subscribers__username")
}
)
msg = EmailMultiAlternatives(
subject=instance.header,
body=instance.text_content,
to=[subscriber.get("subscribers__email")]
)
msg.attach_alternative(html_content, 'text/html')
msg.send()
Related
I'm using Django. In my function accept_request(), I'm trying to select the profile of a user with a certain PrimaryKey. Here's the function:
def accept_request(request, pk):
book = Book.objects.get(id=pk)
print(book.owner.pk)
user = request.user
user_email = request.user.email
owner_profile = UserProfileInfo.objects.get(user=book.owner)
owner_house_number = owner_profile.house_number
owner_phone_number = owner_profile.phone_number
owner_full_name = owner_profile.full_name
r = UserRents.objects.get(book_id=pk)
r.status = 1
r.save()
subject = "{}, your request to rent the book, \"{}\", has been accepted.".format(user, book)
body = "You had requested to rent the book, \"{}\". It's been accepted! You can head over to #{} to get the book. You can contact the owner, {}, at +91 {}. Please return the book in the same condition as it was when you got it.\n Hope you enjoy reading the book!".format(book, owner_house_number, owner_full_name, owner_phone_number)
sender_email = "pfcbookclub#gmail.com"
receiver_email = user_email
password = "Zt2.~[3d*.[Y5&5r"
# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject
message["Bcc"] = receiver_email # Recommended for mass emails
# Add body to email
message.attach(MIMEText(body, "plain"))
text = message.as_string()
# Log in to server using secure context and send email
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, text)
return redirect("/")
This is the UserProfileInfo model in models.py:
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
full_name = models.CharField(max_length=1000, blank=True)
house_number = models.CharField(max_length=5, blank=True)
email = EmailField(max_length=1000, blank=True)
creation_date = models.DateTimeField(default=timezone.now)
phone_number = models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.user.username
And here's the Book model in models.py:
class Book(models.Model):
title = models.CharField(max_length=1000, blank=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
description = models.TextField()
author = models.CharField(max_length=1000, blank=True, null=True)
thumbnail = models.ImageField(upload_to='book_thumbnails', blank=True)
creation_date = models.DateTimeField(default=timezone.now)
status = IntegerField(default=1)
min_age = models.IntegerField(blank=True, null=True)
max_age = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.title
When I go to the URL I've fixed for this function, it gives an error:
DoesNotExist at /main/accept_rent_request/1/
UserProfileInfo matching query does not exist.
The error's at the line where I try to access the UserProfileInfo of the owner of the book:
owner_profile = UserProfileInfo.objects.get(user=book.owner)
Django is not able to access the profile of the owner. What can I do to fix the issue?
First, you can access the user's profile info by doing
owner_profile = book.owner.userprofileinfo
Also, the error stems from your UserProfileInfo not existing. If you want to explictly query it, you can first check if it exists, then assigning it to owner_profile
user_profile_info_query = UserProfileInfo.objects.filter(user=book.owner)
if user_profile_info_query.exists():
owner_profile = user_profile_info_query.first()
You can maybe create a new UserProfileInfo in the shell and assign the user to it so you can test.
I have these models
class Review(models.Model):
# SET_NULL ensures that when a company is deleted, their reviews remains
company = models.ForeignKey(Company, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
# SET_NULL ensures that when a user is deleted, their reviews get deleted too
review_text = models.TextField(max_length=500, verbose_name='Your Review: (Maximum of 200 Words)')
rating = Int_max.IntegerRangeField(min_value=1, max_value=5)
date_added = models.DateField('Review Date', auto_now_add=True)
def __str__(self):
return self.review_text
class Response(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
review = models.ForeignKey(Review, on_delete=models.CASCADE)
response = models.TextField(max_length=200, verbose_name='Your Response')
date_added = models.DateField('Response Date', auto_now_add=True)
def __str__(self):
return self.response
class ResponseReply(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
response = models.ForeignKey(Response, on_delete=models.CASCADE)
review = models.ForeignKey(Review, on_delete=models.CASCADE)
reply = models.TextField(max_length=200, verbose_name="Your Reply")
date_added = models.DateField('Response Date', auto_now_add=True)
def __str__(self):
return self.reply
How to I select ResponseReply belonging to a particular response which also has a foreign key to a particular review.
My view rather returns a list instead of an object of the Review Model since there are more than one review. Below is my view:
def profile_company(request):
print(request.user)
company = get_object_or_404(Company, user=request.user)
review = get_object_or_404(Review, company=company)
responses = get_list_or_404(Response, review=review)
response = get_object_or_404(Response, review=review)
responses = get_list_or_404(Response, review=review)
reply = get_object_or_404(Response, response=response)
company_reviews = company.review_set.all()
total_reviews = len(company_reviews)
print(company.average_rating)
form = ResponseForm()
if request.method == "POST":
form = ResponseForm(request.POST or None)
if form.is_valid:
data = form.save(commit=False)
data.user = request.user
data.review = review
data.save()
return redirect('profile_company')
context = {
'company': company,
'company_reviews': company_reviews,
'total_reviews': total_reviews,
'form': form,
'responses': responses,
'reply': reply,
}
return render(request, 'companyusers/profile_company.html', context)
The problem is when I call response = get_object_or_404(Response, review=review) it fails since the review can be more than one. How do I select a particular response belonging to a particular review. To enable me select associated replies.
Thank you so much!
It all looks good by when i try to add some post to my topic I have an error created_by=user
:
Here is my forms.py
from django import forms
from .models import Topic
class NewTopicForm(forms.ModelForm):
message = forms.CharField(
widget=forms.Textarea(
attrs={'rows': 5, 'placeholder': 'What is on your mind?'}
),
max_length=40000,
help_text='The max length of the text is 4000.')
class Meta:
model = Topic
fields = ['subject', 'message']
and my view function for this form:
def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk)
user = User.objects.first()
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save(commit=False)
topic.board = board
topic.starter = user
topic.save()
post = Post.objects.create(
message=form.cleaned_data.get('message'),
topic=topic,
created_by=user
)
return redirect('board_topics', pk=board.pk)
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'board': board, 'form': form})
models.py
class Post(models.Model):
message = models.TextField(max_length=4000)
topic = models.ForeignKey(Topic, related_name='t_posts', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(null=True)
created_by = models.ForeignKey(Topic, related_name='posts', on_delete=models.CASCADE)
updated_by = models.ForeignKey(User, related_name='+', null=True, on_delete=models.CASCADE)
There is a mistake in your Post model
created_by needs to be a Foreign Key to the User table
created_by = models.ForeignKey(User, related_name='created_by', on_delete=models.CASCADE)
run the following commands after the above changes to reflect the changes in the database
python manage.py makemigrations
python manage.py migrate
I am trying to make Private Message App for my website on django.
Models.py:
class Message(models.Model):
sender = models.ForeignKey(User, related_name='sender')
recipient = models.ForeignKey(User, related_name='recipient')
sent_date = models.DateTimeField(blank=True, null=True)
title = models.CharField(max_length=70, default='Без теми', blank=True, null=True)
body = models.TextField(max_length=10000)
def __str__(self):
return self.title
class Meta:
verbose_name = 'повідомлення'
verbose_name_plural = 'Повідомлення'
Views.py:
#login_required
def write(request):
context = {}
context.update(csrf(request))
context['form'] = WriteMessage()
if request.POST:
write_form = WriteMessage(request.POST)
if write_form.is_valid():
cd = write_form.cleaned_data
if User.objects.filter(username=cd['recipient']).exists():
message = Message(sender = request.user, recipient=User.objects.get(username = cd['recipient']), title=cd['title'], body=cd['body'], sent_date=datetime.now)
message.save()
return redirect('/inbox/')
else:
context['errors'] = ["Not found user with this username"]
return render(request, 'send_message.html', context)
else:
return render(request, 'send_message.html', context)
And when I try to send the message, I get the error: expected string or buffer. But, when I send message from admin page - it works wonderful.
What I must do? Help me, please. Thanks.
My solution is replace sent_date = models.DateTimeField(blank=True, null=True) for sent_date = models.DateTimeField(auto_now_add=True) and deleting sent_date=datetime.now from creating new object in views.py
It seems, that trouble was in different types of data in DateField into models.py and datetime module...
I'm building a job board and I'm using django_messages for private messages.
It works great but I want to auto fill the recipient form field with the username and I don't know why.
Some thoughts?
This is the view which I have candidates that applied for a job position. When I click send message, I need to auto fill the recipient field.
#views.py
class Screening(generic.DetailView):
model = Job
template_name = 'dashboard/screening.html'
def get_context_data(self, **kwargs):
context = super(Screening, self).get_context_data(**kwargs)
context['candidate_list'] = self.object.applied_to.all().order_by('candidate')
return context
#in the template
{% for candidate in candidate_list %}
{% candidate %}
Send message to this candidate
{% endfor %}
The models
#models.py
class Candidate(models.Model):
user = models.OneToOneField(User, primary_key=True)
birth = models.CharField(max_length=50)
...
class Job(models.Model):
candidate = models.ManyToManyField('Candidate', through='CandidateToJob')
title = models.CharField(max_length=500)
...
class CandidateToJob(models.Model):
job = models.ForeignKey(Job, related_name='applied_to')
candidate = models.ForeignKey(Candidate, related_name='from_user')
STATUS_CHOICES = (
('1', 'Not approved'),
('2', 'Approved'),
('3', 'Hired')
)
status = models.CharField(max_length=2, choices=STATUS_CHOICES)
class Meta:
unique_together = ("candidate", "job")
The django_messages app view to send messages
def compose(request, recipient=None, form_class=ComposeForm,
template_name='djangomessages/compose.html', success_url=None, recipient_filter=None):
if request.method == "POST":
sender = request.user
form = form_class(request.POST, recipient_filter=recipient_filter)
if form.is_valid():
form.save(sender=request.user)
messages.info(request, _(u"Message successfully sent."))
if success_url is None:
success_url = reverse('messages_inbox')
if 'next' in request.GET:
success_url = request.GET['next']
return HttpResponseRedirect(success_url)
else:
form = form_class()
if recipient is not None:
recipients = [u for u in User.objects.filter(**{'%s__in' % get_username_field(): [r.strip() for r in recipient.split('+')]})]
form.fields['recipient'].initial = recipients
return render_to_response(template_name, {
'form': form,
}, context_instance=RequestContext(request))
And the model in django_messages app
class Message(models.Model):
subject = models.CharField(_("Subject"), max_length=120)
body = models.TextField(_("Body"))
sender = models.ForeignKey(AUTH_USER_MODEL, related_name='sent_messages', verbose_name=_("Sender"))
recipient = models.ForeignKey(AUTH_USER_MODEL, related_name='received_messages', null=True, blank=True, verbose_name=_("Recipient"))