Modify the class based view object to save - django

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

Related

2 Forms on same model not saving as same user - Django

I'm creating a questionnaire / survey, and have two forms (Model Form) built on the same model. These forms are called on separate views, but when saved they appear as separate users in the database. I'm not sure how to get them so save as the same user, I am already using the ' post = form.save(commit=False), post.user = request.user, post.save()' method to save the forms.
EDIT: Added in an attempt to save to the same instance
Model:
class QuizTakers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
industry_choices = (
(1, 'Service'),
(2, 'Hospitality'),
(3, 'Wholesale/Retail'),
(4, 'Manufacturing'),
(5, 'Agriculture')
)
industry = MultiSelectField(choices=industry_choices, max_length=1, max_choices=1)
company_name = models.CharField( max_length=100)
email = models.EmailField(blank=True)
score = models.FloatField(default=0)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.company_name
Forms:
# Form for getting company name
class QuizTakerForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['company_name']
# Form for getting company industry
class QTIndustryForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['industry']
Views:
# view for getting company name
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
request.session['company_name'] = form.cleaned_data['company_name']
post = form.save(commit=False)
post.user = request.user
post.save()
# redirect to a new URL:
return HttpResponseRedirect('industry/')
# if a GET (or any other method) we'll create a blank form
else:
form = QuizTakerForm()
return render(request, 'ImpactCheck/start.html', {'form': form})
# view for getting industry
class IndustryView(FormView):
template_name = 'ImpactCheck/industry.html'
form_class = QTIndustryForm
success_url = '1/'
def get(self, request):
company_name = request.session['company_name']
this_user=QuizTakers.objects.filter(company_name=company_name).order_by('-timestamp').first()
form=self.form_class(instance=this_user)
company_name = request.session['company_name']
return render(request, 'ImpactCheck/industry.html', {'form': form, 'company_name': company_name})
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
post = form.save(commit=False)
post.user = self.request.user
post.save()
return HttpResponseRedirect('/1')
Firstly, in your def start(request) function, you should consider adding the ID to request.session instead of the company name. Something along the lines of
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
form.instance.user=request.user
form.save()
request.session['obj_id'] = post.id
# redirect to a new URL:
return HttpResponseRedirect('industry/')
Now you can use that id to get both the name of your company, as well as the object.
In your IndustryView(FormView), if you're having trouble with the form instances, it's better to use UpdateView instead of the FormView (Be sure to import UpdateView first)
class IndustryView(UpdateView):
template_name = 'ImpactCheck/industry.html'
model = QuizTakers
fields = ['industry']
success_url = '/1'
def get_object(self):
return QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['company_name'] = QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
return ctx
We use the get_context_data method since you need the company_name in your template. The get_object method in this view, tells django which object is to be updated. By default, it grabs the pk from the url (as a url parameter). But since we store our id in the session, we need to explicitly define this function.
Also, since we switched to UpdateView, you no longer need the QTIndustryForm either.

Auto populate hidden form fields in modelform

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.

Django - How to add sender-name in Email?

What I am trying to achieve is having the senders-name, from the current logged in user with the association name, to show up in the receivers inbox like so:
'associaton-name'#domain.com
I have commented it down below where i tried to achieve it in views.py
Can't seem to find any related solutions after days and hours of work.
Really appreciate your help, folks!
Django: 1.10
Python: 3.6
views.py
class mailPost(FormView):
success_url = '.'
form_class = mailHandler
template_name = 'post/post.html'
def form_valid(self, form):
messages.add_message(self.request, messages.SUCCESS, 'Email Sent!')
return super(mailPost, self).form_valid(form)
def form_invalid(self, form):
messages.add_message(self.request, messages.WARNING,
'Email not sent. Please try again.')
return super(mailPost, self).form_invalid(form)
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
sender = "noreply#domain.com" # Instead of noreply I wish for current requested associaton name
receiver = form.cleaned_data.get('receiver')
cc = form.cleaned_data.get('cc')
bcc = form.cleaned_data.get('bcc')
subject = form.cleaned_data.get('subject')
message = form.cleaned_data.get('message')
time = datetime.now()
asoc_pk = Association.objects.filter(asoc_name=self.request.user.association)
asoc = Association.objects.get(id=asoc_pk)
Email.objects.create(
sender=sender,
receiver=receiver,
cc=cc,
bcc=bcc,
subject=subject,
message=message,
association=asoc,
sentTime=time
)
msg = EmailMultiAlternatives(subject, message, sender, [receiver], bcc=[bcc], cc=[cc])
msg.send()
return self.form_valid(form)
else:
return self.form_invalid(form)
models.py
class Email(models.Model):
sender = models.CharField(max_length=254)
sentTime = models.DateTimeField(auto_now_add=True, blank=False)
subject = models.CharField(max_length=254)
receiver = models.CharField(max_length=254)
cc = models.CharField(max_length=254)
bcc = models.CharField(max_length=254)
message = models.TextField()
association = models.ForeignKey(Association)
class Meta:
db_table = 'Email'
class Association(models.Model):
asoc_name = models.CharField(max_length=50, null=True, blank=True, unique=True)
class Meta:
db_table = 'Association'
class Administrator(AbstractUser):
...
association = models.ForeignKey(Association)
class Meta:
db_table = 'Administrator'
I'm not sure I understand your question correctly. You can access the authenticated user (given you are using the Django Authentication system) by calling self.request.user.
You have to create a relation between Association and the user:
class Association(models.Model):
asoc_name = models.CharField(max_length=50, null=True, blank=True, unique=True)
# Option 1 - if one user can be a member of several associations
members = models.ManyToMany(User)
class Meta:
db_table = 'Association'
or a new model instance if a user can only be a member of one association:
# Option 2
class Membership(Model):
association = models.ForeignKey(Association)
user = models.ForeignKey(User, unique=True)
You get the Association using a direct lookup (or a reverse relation).
# option 1
if form.is_valid():
sender = Association.objects.filter(members=self.request.user).first()
# sender might be None
# option 2
if form.is_valid():
membership = Membership.objects.filter(user=self.request.user).first()
if membership:
sender = membership.association
https://docs.djangoproject.com/en/1.11/topics/db/examples/many_to_many/

Message notification in django

i have simple message model in my study project.
class Message(models.Model):
sender = models.ForeignKey(CustomUser, related_name='sender')
reciever = models.ForeignKey(CustomUser, related_name='reciever')
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
How can I send notification to request.user about new message.
i.e. I need send notification to request.user if I have new Message object with request.user in reciever field
UPD my view:
def dialog(request, user_pk):
sent = Message.objects.filter(reciever_id=user_pk, sender_id=request.user.pk)
recieved = Message.objects.filter(reciever_id=request.user.pk, sender_id=user_pk)
mate = CustomUser.objects.get(pk=user_pk)
dialog_list = sorted(chain(sent, recieved), key=lambda a:a.created_at)
if request.POST:
form = MessageForm(request.POST)
if form.is_valid():
f = form.save(commit=False)
f.sender = CustomUser.objects.get(pk=request.user.pk)
f.reciever = CustomUser.objects.get(pk=user_pk)
form.save()
else:
form=MessageForm()
return render(request, 'dialog.html', {'sent':sent,
'recieved':recieved, 'form':form, 'mate':mate, 'dialog_list':dialog_list})
look's like this:
I solved this problem by making some ugly logic
Update my model with boolean field:
class Message(models.Model):
sender = models.ForeignKey(CustomUser, related_name='sender')
reciever = models.ForeignKey(CustomUser, related_name='reciever')
text = models.TextField(verbose_name='')
created_at = models.DateTimeField(auto_now_add=True)
is_readed = models.BooleanField(default=False)
In view I create set for my message and add users here, who have messages with is_readed == False
It become True when we load dialog page with this user
I never working in real project, but I think this solution unacceptable here.
Anyway in my first study project it's worked, maybe it helped some newbies, like meemphasized text
def dialog(request, user_pk):
sent = Message.objects.filter(reciever_id=user_pk, sender_id=request.user.pk)
recieved = Message.objects.filter(reciever_id=request.user.pk, sender_id=user_pk)
not_readed = set()
for message in recieved:
message.is_readed = True
message.save()
for message in Message.objects.filter(reciever_id=request.user.pk):
if message.is_readed ==False:
not_readed.add(CustomUser.objects.get(pk=message.sender_id))
dialog_list = sorted(chain(sent, recieved), key=lambda a:a.created_at)
if request.POST:
form = MessageForm(request.POST)
if form.is_valid():
f = form.save(commit=False)
f.sender = CustomUser.objects.get(pk=request.user.pk)
f.reciever = CustomUser.objects.get(pk=user_pk)
form.save()
else:
form=MessageForm()
return render(request, 'dialog.html', {'sent':sent,
'recieved':recieved, 'form':form, 'mate':mate,
'dialog_list':dialog_list, 'not_readed':not_readed})

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)