I do apologise for all the questions I'm posting today, but I'm at my wits end on this one.
I'm trying to make a Q&A thing for a video site, and I'm trying to get the question to submit via AJAX.
Question model:
class Question(models.Model):
user = models.ForeignKey(User, editable=False)
video = models.ForeignKey(Video, editable=False)
section = models.ForeignKey(Section, editable=False)
title = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
ModelForm:
class QuestionForm(ModelForm):
def __init__(self, video, *args, **kwargs):
super(QuestionForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['section'].queryset = Section.objects.filter(video=video)
class Meta:
model = Question
POST parameters sent by jQuery's AJAX request (the video parameter is added by the Javascript code):
section=6&title=test&description=test&video=1
And finally, here's the view I'm working on to handle the submit:
def question_submit(request):
u = request.user
if u.is_authenticated():
q=QuestionForm(request.POST)
if q.is_valid():
logger.debug("YES!")
else:
logger.debug("NO!")
f=q.save(commit=False)
f.user=u
f.video_id=int(request.POST['video'])
f.save()
return HttpResponse("OK")
else:
return JsonResponse({'failed': 'You are not logged in. Try logging in in a new tab, then re-submit your question.'})
As suggested by the docs, I'm saving with commit=false so that I can modify the object.
I have two problems:
When it reaches q.is_valid(), it throws the error "'QuestionForm' object has no attribute 'cleaned_data'".
If I take out the q.is_valid() bit, f.save() succeeds, but it inserts a blank row into the database.
To anyone who can help, I owe you my sanity.
You aren't passing in video in the view:
forms.py
def __init__(self, video, *args, **kwargs):
views.py
q=QuestionForm(request.POST)
as video is a positional argument, I'd imagine it is interpreting request.POST as the video?
You could change video to a keyword argument:
def __init__(self, video=None, *args, **kwargs):
if video:
...
as mordi metions, you should check if a) it's a valid POST, and b) it's an ajax request
def question_submit(request):
if request.method == "POST" and request.is_ajax():
...
It's look like your request.POST is empty. Are you sure that the data is sent by POST method?, check
if request.method == 'POST:
or use
q=QuestionForm(request.REQUEST)
to get POST/GET data.
Related
I have a contact form on my Django site and when submitted it goes to the success url but the email is not sent, and the logging set up in the form_valid function is never called.
Here is the code for the view:
class ContactView(FormView):
form_class = ContactForm
template_name = "contact.html"
success_url = "/contact-sent/"
def form_valid(self, form):
message = "{name} / {email} said: ".format(
name=form.cleaned_data.get('name'),
email=form.cleaned_data.get('email'))
message += "\n\n{0}".format(form.cleaned_data.get('message'))
recipients = [recipient for recipient in settings.LIST_OF_EMAIL_RECIPIENTS]
try:
send_mail(
subject=form.cleaned_data.get('subject').strip(),
message=message,
from_email='XXX#XXX.com'
recipient_list=recipients,
)
logger = logging.getLogger(__name__)
logger.info("Contact Email sent successfully")
except Exception as e:
logger = logging.getLogger(__name__)
logger.warning("Contact Email failed to send\nInfo: %s" % e)
return super(ContactView, self).form_valid(form)
and the form, which is a model form using floppyforms and crispyforms:
class ContactForm(ffuture.ModelForm):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-contactForm'
self.helper.form_class = 'contact-form'
self.helper.form_method = 'post'
self.helper.form_action = 'submit-feedback'
self.helper.form_tag = True
self.helper.layout = Layout(
Fieldset(
_('Contact Us'),
Field('name', placeholder=_('Name'), css_class='input-medium'),
Field('email', placeholder=_('Email'), css_class='input-xlarge'),
Field('subject', placeholder=_('Subject'), css_class='input-xlarge'),
Field('message', placeholder=_('Add a message'), rows='5', css_class='input-xlarge'),
),
)
self.helper.add_input(Submit('submit', _('Submit')))
class Meta:
model = Feedback
fields = ('name', 'email', 'subject', 'message')
and the model:
#python_2_unicode_compatible
class Feedback(models.Model):
subject = models.CharField(max_length=100)
message = models.TextField(max_length=500)
name = models.CharField(max_length=100)
email = models.EmailField()
creation_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.topic
class Meta:
verbose_name_plural = _("Feedback")
verbose_name = _("Feedback")
The emails are never sent, and the Feedback model is never updated in the admin.
Anyone have any ideas as to why this would be happening? I've been pouring over the code and looking at other examples and none of them seem much different from what I have. I am stumped as to why it is not sending the emails, nor calling any of the logging in the form_valid method.
Ideally I want it to send the contact email to the recipients, and also save the information entered into the Feedback model.
Two other things that may be relevant:
The site is currently running on Apache and for the from_email set in the view I never configured any credentials for it. I am unsure of where to do this. But even if that's the reason the email is not being sent, I don't see why the Feedback model would not be updated.
Thanks for any help you guys can provide, I've been stuck on this for a bit now.
EDIT:
I was thinking it could be the send_mail function that is the issue, but I added logging above the try block and that wasn't called either, so I am now sure that the form_valid method is never being called.
As for the Feedback model not being saved, I realized this is probably because I am never actually saving the form.
I am a bit confused here, because I am using a model form for the contact so the user submitting the form is not logged in. The objective was to both send the email, and store the results in the database. But I can't seem to figure out how I should go about saving the modelform without a valid user.
Would it be enough to just do
feedback = form.save()
inside my form_valid method in the ContactView? Or do I want a save method inside my model form?
The solution was to just call
form.save()
and store the model. The user being logged in did not matter as the fields on the model don't reference a logged in user at all.
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
Suppose I have a set of records which I know to be unique based on some other record and an e-mail, thusly:
class Signup(models.Model):
activity = models.ForeignKey(Activity, related_name='activities')
name = models.CharField(max_length=100)
email = models.EmailField()
# many addtional fields here not relevant to question
Then, I have a model form:
class SignupForm(forms.ModelForm):
class Meta:
model = Signup
exclude = [ 'activity' ]
def __init__(self, *args, **kwargs):
self.activity = kwargs.pop('activity')
def save(self, *args, **kwargs):
kwargs['commit'] = False
m = super(SignupForm, self).save(*args, **kwargs)
m.activity = self.activity
m.save()
return m
Suppose a user goes in a fills out the form under the activity, then realizes they made an error in the form, clicks the back button, makes changes, then clicks submit again.
Without any modifications to the code above, a duplicate record for that activity and email would be created.
What I want to know is how I can force the form to update, rather than create, a record if it finds a match for the entered e-mail.
I tried this code:
class SignupForm(forms.ModelForm):
class Meta:
model = Signup
exclude = [ 'activity' ]
def __init__(self, *args, **kwargs):
self.activity = kwargs.pop('activity')
def save(self, *args, **kwargs):
kwargs['commit'] = False
try:
self.instance = Signup.objects.get(email=self.cleaned_data['email'], activity=self.activity)
except Signup.DoesNotExist:
pass
m = super(SignupForm, self).save(*args, **kwargs)
m.activity = self.activity
m.save()
return m
However, looks like this causes the form to ignore all new information for some reason (I have debug toolbar running and examining the query confirms that none of the fields are being changed!)
Is there an accepted way of handling this?
Further request
Is there any way to do this while still using the ModelForm's built-in save function? So far the answers seem to suggest that this is impossible, which is, I'm sorry, ridiculous.
Replace
try:
self.instance = Signup.objects.get(email=self.cleaned_data['email'], activity=self.activity)
except Signup.DoesNotExist:
pass
With:
obj, created = Signup.objects.get_or_create(\
email=self.cleaned_data['email'],
activity=self.activity)
if created:
print 'its a new one, hooray!'
else:
print 'the object exists!'
More information on get_or_create.
I'm going through a django tutorial to create a wiki and I'm a little stumped on what's happening in the view below. Specifically, this part:
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
msg = "Article saved successfully"
messages.success(request, msg, fail_silently=True)
return redirect(article)
Here are my questions:
what is being instantiating when you write article = form.save(commit=False) and what does the argument, (commit=False) mean?
Where does request.user come from and what does it do?
I could also use an explanation for article.save()
where does messages.success come from?
Sorry for all the questions, but the tutorial is a little sparse on details :(.
Here's the model:
class Article(models.Model):
"""Represents a wiki article"""
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=50, unique=True)
text = models.TextField(help_text="Formatted using ReST")
author = models.ForeignKey(User)
is_published = models.BooleanField(default=False, verbose_name="Publish?")
created_on = models.DateTimeField(auto_now_add=True)
objects = models.Manager()
published = PublishedArticlesManager()
def __unicode__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Article, self).save(*args, **kwargs)
#models.permalink
def get_absolute_url(self):
return ('wiki_article_detail', (), { 'slug': self.slug })
Here's the full view:
#login_required
def add_article(request):
form = ArticleForm(request.POST or None)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
msg = "Article saved successfully"
messages.success(request, msg, fail_silently=True)
return redirect(article)
return render_to_response('wiki/article_form.html',
{ 'form': form },
context_instance=RequestContext(request))
what is being instantiating when you write article =
form.save(commit=False) and what does the argument, (commit=False)
mean?
Saving a modelform inserts/updates the data in the database and returns the model istance (in this case an article instance). A modelform maps a form to a model. However, sometimes you may want to add some extra stuff that does not come directly from the form. So, to prevent two updates, you do not commit to the database by specifying commit=False, the changes will be made to the database when you do a .save() on instance, instead.
Where does request.user come from and what does it do? request.user refers to the currently logged in user (who is making this request).
I could also use an explanation for article.save() - inserts/updates the article fields to the database.
where does messages.success come from? messages framework is just for passing error/success/informative messages using cookies and sessions.
I want to populate two foreign key fields in one of my forms. The relevant bit of code is as below:
if request.method == 'POST':
form = IssuesForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.save()
else:
form = IssuesForm(initial={'vehicle': stock_number, 'addedBy': request.user, })
vehicle points to the Vehicle class. addedBy is to contain the currently logged in user.
However the drop downs aren't initialized as I want...I still have to select the vehicle and user. From this I have two questions:
What could be the problem?
What is the best way to make these forms read-only?
EDIT 1
The IssueForm class looks like this so far:
class Issues(models.Model):
vehicle = models.ForeignKey(Vehicle)
description = models.CharField('Issue Description', max_length=30,)
type = models.CharField(max_length=10, default='Other', choices=ISSUE_CHOICES)
status = models.CharField(max_length=12, default='Pending',
choices=ISSUE_STATUS_CHOICES)
priority = models.IntegerField(default='8', editable=False)
addedBy = models.ForeignKey(User, related_name='added_by')
assignedTo = models.CharField(max_length=30, default='Unassigned')
dateTimeAdded = models.DateTimeField('Added On', default=datetime.today,
editable=False)
def __unicode__(self):
return self.description
Form Class
class IssuesForm(ModelForm):
class Meta:
model = Issues
exclude = ('assignedTo')
For your second question, are you wanting to make the addedBy field read-only? If so, don't add it to your form (it'll never be read-only if you present it to the user, e.g. Firebug). You can instead populate it inside your save method.
if request.method == 'POST':
form = IssuesForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
issue = form.save(commit=False)
issue.addedBy = request.user
# any other read only data goes here
issue.save()
else:
form = IssuesForm(initial={'vehicle': stock_number}) # this is related to your first question, which I'm not sure about until seeing the form code
To make a form read only: on your form class, overwrite the __init__method to disable html fields:
def __init__(self, *args, **kwargs):
super(IssuesForm, self).__init__(*args, **kwargs)
for key in self.fields.keys():
self.fields[key].widget.attrs = {'disabled': 'disabled'}
Makes sure you also don't listen for POST requests, if so, don't save the form.
You can further customize the __init__method to take some arguments and set fields to these values after the super method has been called.