1.As a part of learning django i am trying to send a mail to an email id using pre_save signal.
2.A mail should sent to an email id which is mentioned in the field.I dont't have a proper way to do using signals.Here i am giving the models.py and views.py.
views.py
def addbook():
form = BookForm
if request.POST:
form = BookForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
form.save()
return redirect('/index/')
return render_to_response('addbook.html',{ 'form':form },context_instance=RequestContext(request))
models.py
class Book(models.Model):
book_id=models.AutoField(primary_key=True,unique=True)
book_name=models.CharField(max_length=30)
author_name=models.CharField(max_length=30)
publisher_name=models.CharField(max_length=40)
email = models.EmailField()
bookref = models.CharField(max_length=10)
class Meta:
db_table = u'Book'
def __unicode__(self):
return "%d %s %s %s %s" % (self.book_id,self.book_name, self.author_name,self.publisher_name,self.email,self.bookref)
my requirement is an email should send automatically to the id in the field while submitting the book details.
An example about this to do will be great help.
Thanks
Under the Book models, create the signal function.
class Book(models.Model):
[..........]
def send_update(sender, instance, created, **kwargs):
if instance.author_name:
message = "Book is updated"
subject = "Updates"
send_mail(subject, message, your_email,
[instance.email,])
post_save.connect(send_update, sender=Book)
Related
As a beginner in a contact list project I could not pass the instance of the contact profiles to the phone list form. When I want to add several phone numbers to a person I need the person's instance to be loaded to the add-phone form. when I call the function 'addphone' the form opens a new phone for a list of contacts.
models.py
class contact_names(models.Model):
first_name=models.CharField(max_length=20,null=True,blank=True)
last_name=models.CharField(max_length=20)
area_l=(
('TWO','TWO'),
.
.
)
dep_l=(
.
.
('Other','Other'),
)
area=models.CharField(max_length=22,choices=area_l,null=True,blank=True)
DEP =models.CharField(max_length=22, blank=True, null=True, choices=dep_l)
Subdivision =models.CharField(max_length=20, blank=True, null=True)
created=models.DateTimeField(auto_now_add=True)
id=models.UUIDField(default=uuid.uuid4, unique=True,editable=False,primary_key=True)
def __str__(self) -> str:
return self.last_name
def __str__(self) -> str:
return self.first_name
class contact_phone(models.Model):
p_type=(
('Fixed Phone','Fixed Phone'),
('Cell Phone','Cell Phone'),
)
typep=models.CharField(max_length=15, choices=p_type)
person=models.ForeignKey(contact_names, on_delete=models.CASCADE)
phone=models.CharField(max_length=15)
def __str__(self) -> str:
return self.phone
views.py
def user(request):
form=newuser()
if request.method =='POST':
form=newuser(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
return render(request,'apptwo/form_page.html',{'form':form})
def addphone(request,pk):
ps=contact_names.objects.get(id=pk)
form=newphone(instance=ps)
if request.method =='POST':
form=newphone(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
return render(request,'apptwo/form_page2.html',{'form':form})
forms.py
class newuser(forms.ModelForm):
class Meta:
model= contact_names
fields='__all__'
class newphone(forms.ModelForm):
class Meta:
model=contact_phone
fields=['person','typep','phone']
It looks like you're trying to create a phone number form that is associated with a specific contact profile. In your addphone view, you are correctly fetching the contact profile from the database using the primary key (pk) passed in through the URL, but in the POST portion of the view, you are creating a new form without passing in the instance of the contact profile that you fetched earlier.
To fix this, you should update the POST portion of the addphone view to use the instance of the contact profile when creating the phone number form:
if request.method =='POST':
form=newphone(request.POST,instance=ps)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
Also, In your form, you should define the foreign key to person and it should be hidden in the form,
class newphone(forms.ModelForm):
person = forms.ModelChoiceField(queryset=contact_names.objects.all(), widget=forms.HiddenInput())
class Meta:
model=contact_phone
fields=['person','typep','phone']
This way when you save the form, the person foreign key will be automatically set to the instance of the contact you passed.
I have found a way but with minor issues.
Still when I want to add the first phone to the particular person, the person dropdown list is shown as '------', in form as default, but the instance is OK. And when I want to add a new second phone, the phone field in the form is not empty.
I'll be very glad if someone helps me. I appreciate the posted answer, it helped but didn't solve it completely.
views.py
def addphone(request,pk):
ps=contact_names.objects.filter(id=pk)
for s in ps:
pd=s.contact_phone_set.all()
if pd.first()== None:
form=newphone(instance=s)
form.fields['person'].queryset=ps
else:
form=newphone(instance=pd.last())
form.fields['phone'].queryset=None # <------This has no effect!
if request.method =='POST':
form=newphone(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
return render(request,'apptwo/form_page2.html',{'form':form})enter code here
forms.py
class newphone(forms.ModelForm):
# person = forms.ModelChoiceField(queryset=contact_names.objects.all(), widget=forms.HiddenInput())# #THis didn't Help Since the field is required.
class Meta:
model=contact_phone
p_type=(
('Fixed Phone','Fixed Phone'),
('Cell Phone','Cell Phone'),)
fields=['person','typep','phone']
labels = {
'typep': '',
}
widgets = {
'typep': forms.RadioSelect(choices=p_type)
}
I have used the default Django admin panel as my backend. I have a Blogpost model. What I am trying to do is whenever an admin user saves a blogpost object on Django admin, I need to send an email to the newsletter subscribers notifying them that there is a new blog on the website.
Since Django admin automatically saves the blog by calling the save function, I don't know where to write send email api logic. Hope I have explained it well.
My Blogpost:
class BlogPost(models.Model):
author = models.CharField(max_length=64, default='Admin')
CATEGORY_CHOICES = (
('travel_news', 'Travel News',),
('travel_tips', 'Travel Tips',),
('things_to_do', 'Things to Do',),
('places_to_go', 'Places to Go'),
)
image = models.ImageField(blank=True, null=True)
title = models.CharField(max_length=255)
categories = models.CharField(max_length=64, choices=CATEGORY_CHOICES, default='travel_news')
caption = models.CharField(max_length=500)
content = RichTextUploadingField()
# todo support for tags
# tags = models.CharField(max_length=255, default='travel') #todo
tag = models.ManyToManyField(Tag)
date_created = models.DateField()
I have overwritten the Django admin form by my model form like this.
class BlogForm(forms.ModelForm):
CATEGORY_CHOICES = (
('travel_news', 'Travel News',),
('travel_tips', 'Travel Tips',),
('things_to_do', 'Things to Do',),
('places_to_go', 'Places to Go'),
)
# categories = forms.MultipleChoiceField(choices = CATEGORY_CHOICES)
class Meta:
model = BlogPost
fields = ['author','image', 'title','categories',
'caption','content','tag','date_created']
#register(BlogPost)
class BlogPostAdmin(ModelAdmin):
# autocomplete_fields = ['author']
def edit(self, obj):
return format_html('<a class="btn-btn" href="/admin/blogs/blogpost/{}/change/">Change</a>', obj.id)
def delete(self, obj):
return format_html('<a class="btn-btn" href="/admin/blogs/blogpost/{}/delete/">Delete</a>', obj.id)
list_display = ('categories', 'title', 'date_created','edit', 'delete')
icon_name = 'chrome_reader_mode'
# inlines = [TagInline]
form = BlogForm
I have written a view, but it doesnt make sense since there is no any url calling it as we dont need any view and url when saving from the django admin itself.
from apps.blogs.admin import BlogForm
def blogform(request):
if request.method == 'POST':
form = BlogForm(request.POST)
if form.is_valid():
form.save()
Without this logic too, the form is working fine.
My subscribers model:
class Subscribers(models.Model):
email = models.EmailField(unique=True)
date_subscribed = models.DateField(auto_now_add=True)
def __str__(self):
return self.email
class Meta:
verbose_name_plural = "Newsletter Subscribers"
This is how you can send and email to subscribers after a new Post is created:
from django.db.models.signals import post_save
from django.dispatch import receiver
class BlogPost(models.Model):
subscribers = models.ManyToManyField(User) # <-- subscribers
title = models.CharField(max_length=255)
class BlogPost(models.Model):
blog = models.ForeignKey(Blog, on_delete= models.CASCADE) #<-- blog
title = models.CharField(max_length=255)
# ....
# binding sinal:
#receiver(post_save, sender=BlogPost)
def send_mail_to_subs(sender, instance, created, **kwargs):
if Not created:
return
for mysub in instance.blog.subscribers.all():
send_mail(
f'New Post {instance.title}',
'Hi .... ',
'from#example.com',
[sub.email],
)
Be free to adapt this sample to your custom scenario. For example, maybe do you have other relations to get blog or subscribers.
About performance.
If do you have lots of subscribers, this is not a real solution. For massive mailing do you need a background process to send mails.
For example, you can add a field to store if a post was notified to subscribers:
class BlogPost(models.Model):
blog = models.ForeignKey(Blog, on_delete= models.CASCADE) #<-- blog
title = models.CharField(max_length=255)
notified = models.BooleanField(editable=False, default=False)
Then execute a process each X minutes to get not notified posts and send mails to all subscribers.
Do you have two options:
Custom command on system cron
Celery
You can override the save method in your model.
class BlogPost(models.Model):
author = models.CharField(max_length=64, default='Admin')
.....
def save(self, *args, **kwargs):
if self.pk:
# send mail here
send_mail()
return super().save(*args, **kwargs)
Or you can write the signals also.
#receiver(post_save, sender=BlogPost)
def send_mail_to_user(sender, instance, created, **kwargs):
if created:
# send mail
send_mail()
Better to use signals. If you need to send it to all subscribers you need to create a loop in signal function.
You can create a signal in blog post view like this:
#receiver(post_save, sender=BlogPost)
def send_mail_to_subs(sender, instance, created, **kwargs):
if created:
for subs in instance.author.subscribed.all():
send_mail(
f'New Post from {instance.author}',
f'Title: {instance.post_title}',
'youremail',
[subs.email],
)
Good coding :)
I currently have the model such
class Newsletter(models.Model):
email = models.EmailField(null=False, blank=True, max_length=200, unique=True)
conf_num = models.CharField(max_length=15)
confirmed = models.BooleanField(default=False)
def __str__(self):
return self.email + " (" + ("not " if not self.confirmed else "") + "confirmed)"
And I have the class based view
class NewsletterView(SuccessMessageMixin, CreateView):
template_name = 'newsletter.html'
success_url = reverse_lazy('newsletter')
form_class = NewsletterRegisterForm
success_message = "Check your inbox for the verification email"
def form_valid(self, form):
self.conf_num = random_digits()
subject = 'Newsletter Confirmation',
html_content = 'Thank you for signing up for my email newsletter! \
Please complete the process by \
<a href="{}/confirm/?email={}&conf_num={}"> clicking here to \
confirm your registration</a>.'.format(self.request.build_absolute_uri('/confirm/'),
self.email,
self.conf_num)
sender = "noreply#example.com"
recipient = form.cleaned_data['email']
msg = EmailMultiAlternatives(subject, html_content, sender, [recipient])
msg.send()
return super().form_valid(form)
I'm slightly confused as to how I would be able to set via the class based view, the conf_num? Would I have to say in my form_valid function correctly call self.conf_num = number?
When I try either of these methods I either get that the email is not unique or that the newsletter object has no email. Any help would be apprecicated.
I would choose this method,
class NewsletterView(SuccessMessageMixin, CreateView):
template_name = 'newsletter.html'
success_url = reverse_lazy('newsletter')
form_class = NewsletterRegisterForm
success_message = "Check your inbox for the verification email"
def send_email(self, conf_num):
# gather relevant data for email compose
# you can use function args or instance attributes
# and then, send mail from here
email.send()
def form_valid(self, form):
response = super().form_valid(form) # calling the `super()` method on the top will be the best, in this case
conf_num = random_digits()
self.send_email(conf_num)
# after sending the mail, access the `self.object` attribute
# which hold the instance which just created
self.object.conf_num = conf_num # assign the value
self.object.save() # call the save() method to save the value into the database
return response
I hope the comments are self-explanatory here :)
In this case, the form is the object that holds the Newsletter instance.
def form_valid(self, form):
form.conf_num = random_digits()
newsletter = form.save()
So my model, form, and view are working mostly. View works and sending the email works. The "message" is saved but I cannot get the message_to and message_from to save. It is supposed to save the usernames. I can get everything to save, but cannot get the message saved to the database WITH the to and from usernames. I am trying to only have 1 field in the message. "Content". The to and from should be hidden and auto-populated. I appreciate any other set of eyes on this. Thank you.
'models.py'
class Message(models.Model):
message_content = models.TextField()
message_to = models.ForeignKey(User, on_delete=models.CASCADE, related_name='message_to')
message_from = models.ForeignKey(User, on_delete=models.CASCADE, related_name='message_from')
date_created = models.DateTimeField(default=timezone.now)
unread = models.BooleanField(default=True)
'forms.py'
class MessageSellerForm(forms.ModelForm):
class Meta:
model = Message
'views.py'
def ad_detail(request, *args, **kwargs):
template_name = 'x_ads/ad_detail.html'
ad = get_object_or_404(Ad, pk=kwargs['pk'])
ad.increment_view_count()
if request.method == 'POST':
message_form = MessageSellerForm(data=request.POST)
message_form.message_from = request.user.username
message_form.message_to = ad.creator.username
if message_form.is_valid():
subject = 'Message about your ad. ' + ad.title
from_email = request.user.email
to_email = ad.creator.email
message = 'You have a message about one of your ads waiting for you!'
send_mail(subject=subject, message=message, from_email=from_email,
recipient_list=[to_email], fail_silently=False)
messages.success(request, your message has been sent.')
message_form.save()
return HttpResponseRedirect(request.path_info)
else:
message_form = MessageSellerForm()
return render(request, template_name, {'ad': ad, 'message_form': message_form})
I think I see what you're trying to do there, but there are other ways that I think will be a bit easier.
https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/#the-save-method
You could instead:
# create the django object in memory, but don't save to the database
message = message_form.save(commit=False)
message.message_from = request.user.username
message.message_to = ad.creator.username
# now save it to the database
message.save()
If you do that you won't need the assignments to the message form further up:
message_form.message_from = request.user.username
message_form.message_to = ad.creator.username
EDIT
You might also need to modify your MessageSellerForm to not include the message_from and message_to fields so that validation will work. That's OK because you know that you'll be assigning the right values to those fields after form validation but before saving to the database.
I have a model into I send an email each time this model is edited, with post_save, nice!
I can recover fields from my model to display them in the email, good.
But I would display information about the user connected, logged (username...).
I tried some syntaxes, without success, please can you help me?
In model.py, at the end of my model related:
#receiver(post_save, sender=User)
def save(self, **kwargs):
text = 'Hello\n%s %s has just been edited.' % (self.first_name, self.last_name)
my_from = 'webmaster#hg-map.fr'
my_subject = 'Prospect Edit (from test instance, home)'
email = EmailMessage(my_subject, text, my_from, to=['xxx#xxx.fr'])
email.send()
Yes, now I do it, at the bottom of models.py:
#receiver(post_save, sender=MyModelRelated)
def save(sender, instance, **kwargs):
text1 = 'Hello\n%s %s has just been edited.' % (instance.first_name, instance.last_name)
form_admin_url = 'Admin form: https://xxx.fr/admin/prospect/prospect/'+str(instance.id)+'/change/'
footer = 'This message was sent automatically, do not answer.'
my_from = 'webmaster#xxx.fr'
my_subject = 'Prospect Edited (%s %s)' % (instance.first_name, instance.last_name)
email = EmailMessage(my_subject, text1+'\n'+form_admin_url+'\n'+footer, my_from, to=['geo#xxx.fr'])
email.send()