User information in a model post_save email - django

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()

Related

Django: Email isn't sent in built-in PasswordResetView

I'm overriding the built-in PasswordResetView but the email isn't sent. I'm currently using django.core.mail.backends.console.EnailBackend but the email's content doesn't show up on console.
My code is like this
class CustomPasswordResetView(PasswordResetView):
email_template_name = 'accounts/password_reset_email.html'
form_class = CustomForm
template_name = 'accounts/password_reset.html'
subject_template_name = 'accounts/password_reset_subject.txt'
title = 'Custom Title'
success_url = reverse_lazy('accounts/password_reset_done')
It redirects to the password_reset_done as expected but the email doesn't show on concole.
Is there something I missed? As long as I see the Django's code, I cannot find the part handling with sending email in PasswordResetView Do I have to write email functionality manually?
forms.py
class CustomPasswordResetForm(PasswordResetForm):
def __init__(self, *args, **kwargs):
super(CustomPasswordResetForm, self).__init__(*args, **kwargs)
...
def save(self, ...):
super().save()
Problem is that, the email is sent from the form, not the view. So if you are using CustomForm, its better to implement the send email method in the form like this:
class CustomForm(forms.Form):
...
def send_mail(self):
return send_mail(
'Subject here',
'Here is the message.',
'from#example.com',
[self.cleaned_data.get('email')],
fail_silently=False,
)
def is_valid(self):
valid = super(CustomForm, self).is_valid()
if valid:
self.send_email()
return valid
Or you can override from PasswordResetForm and put your customization there.

Django Best practices on overriding foreign key form widget to receive user input

I defined a model that has some foreign keys to other models. Such that I have the following in models.py:
class Appelation(models.Model):
appelation = models.CharField(max_length=100,
verbose_name=_('Appelation'),
validators=[non_numeric],
blank=True,
unique=True
)
class Wine(models.Model):
appelation = models.ForeignKey(ForeignKeyModel, null=True, blank=True, verbose_name=_('Appelation'))
forms.py
class WineForm(ModelForm):
class Meta:
model = Wine
appelation= CharField(widget=TextInput)
views.py
class WineCreateView(WineActionMixin, LoginRequiredMixin, CreateView):
model = Wine
form_class = WineForm
action = 'created'
def post(self, *args, **kwargs):
self.request.POST = self.request.POST.copy() # makes the request mutable
appelationForm = modelform_factory(Appelation, fields=('appelation',))
form_dict = {
'appelation': appelationForm
}
for k, modelForm in form_dict.iteritems():
model_class = modelForm.Meta.model
log.debug('current model_class is: %s' % model_class)
log.debug('request is %s' % self.request.POST[k])
try:
obj = model_class.objects.get( **{k: self.request.POST[k]} )
log.debug("object exists. %s pk from post request %s " % (model_class,obj.pk))
self.request.POST[k] = obj.id
except ObjectDoesNotExist as e:
log.error('Exception %s' % e)
f = modelForm(self.request.POST)
log.debug('errors %s' % f.errors)
if f.is_valid():
model_instance = f.save()
self.request.POST[k] = model_instance.pk
return super(WineCreateView,self).post(self.request, *args, **kwargs)
Basically, what the view code does is, it tries to create a new Appelation model instance ( which is a fk to Wine) if the one we passed does not exist. and it returns the pk in the field, since we expect a pk, not a string as input.
I want to create appelationForm, because I have some custom validators I need to apply to validate the foreignKey input.
The limitations I see now, Is that I don't see how I can attach the validation errors from appelationForm to the ones of the main form so that they are displayed instead of the ones we would typically have from a foreignKey field.
To see the full example code:
https://github.com/quantumlicht/django-wine/blob/master/project/corewine/models.py
https://github.com/quantumlicht/django-wine/blob/master/project/corewine/forms.py
https://github.com/quantumlicht/django-wine/blob/master/project/corewine/views.py
What you should do is write a clean_appelation method on your WineForm, which comprehensively validates the input according to your criteria, i.e. either an existing Appelation id, or a new Appelation name, and raises the appropriate errors. Then in your view, you can assume the form data is valid and will work. This should give you something to start off with:
class WineForm(ModelForm):
...
appelation= CharField(widget=TextInput)
def clean_appelation(self):
data = self.cleaned_data['appelation']
if data.isdigit():
# assume it's an id, and validate as such
if not Appelation.objects.filter(pk=data):
raise forms.ValidationError('Invalid Appelation id')
else:
# assume it's a name
if ...:
raise forms.ValidationError('Invalid Appelation name')
return data

Trigger an event (send a mail) when all the object in the relations are saved in django

I'm developing an eCommerce in django.
The issue is about Order and RowOrder computations:
class Order(Model):
STATUS = {
'NPAI': 'Not paid',
'PAID': 'Paid',
'SHIP': 'Shipped',
}
status = CharField(max_length=4, choices=STATUS.items(), editable=False, default='NPAI')
cod = CharField(max_length=30, unique=True, db_index=True, editable=False)
total_price = DecimalField(max_digits=4, decimal_places=2, default=Decimal('0.00'), editable=False)
vat = DecimalField(max_digits=4, decimal_places=2, default=Decimal('0.00'), editable=False)
[...]
class RowOrder(Model):
article = ForeignKey(Article)
order = ForeignKey(Order)
[...]
def save(self, *args, **kwargs):
# update order
with transaction.commit_on_success():
order_Locked = Order.objects.select_for_update().get(id=self.order.id)
order_Locked.total_price += self.price
order_Locked.vat_price += self.price - self.price/(1 + order_Locked.vat/100)
order_Locked.save()
As you can see, I update the values of an order every time a roworder is saved. This works well, but now I've to send an order confirm email when the order is created, but if I send the email in Order.save():
def save(self, *args, **kwargs):
if self.status == 'NPAI':
super(Order, self).save(*args, **kwargs)
send_order_confirm(self)
then, rightly, the self.roworder_set in that time is empty.
I've chosen a wrong approach or actually is there anything that I can do to achieve my goal? Thank you so much.
You should use post_save on Order for this. Also, you want to send the email only when order is created, so:
def send_order_confirmation(sender, **kwargs):
created = kwargs['created']
if created:
#logic for sending email
post_save.connect(send_order_confirmation, sender=Order)
Thanks to akshar raaj comments I think that I've solved my issue:
I've to trigger the event from django-admin and not in the model:
class OrderAdmin(ModelAdmin):
[...]
def save_formset(self, request, form, formset, change):
super(OrderAdmin, self).save_formset(request, form, formset, change)
order = formset.instance
if order.status == 'NPAI':
send_order_confirm(order)
useful link to the django official doc
clearly if I need that event from a view I can directly call send_order_confirm in the view

django sending mail using pre_save

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)

How insert id in username field

I want username field automatically filled with this:
username = email[0] + str(id)
where id is the id of the user object
Is it possible ?
Thanks :)
You can use hooks to achieve this.
It would be something like this (not tested):
from django.db import models
class User(models.Model):
email = model.EmailField()
username = models.CharField(max_length=80)
def save(self):
if not self.id:
self.username = ''
super(User, self).save()
self.username = "%i%s" % (self.id, self.email)
super(User, self).save()