I have the following test...
def test_contact_form(self):
form_data = {'name': 'Reiss Johnson',
'email': 'reissjohnson#test.com',
'_type': 'General Enquiry',
'content': 'This is some content'}
form = ContactForm(data=form_data)
self.assertTrue(form.is_valid())
This was originally passing as '_type' was a CharField, however it is now a ModelChoiceField & so does anybody know why this is failing? Im guessing im inputting the '_type' into the dictionary incorrectly now?
How could I get the above test to pass again?
My form is as so...
class ContactForm(forms.Form):
name = forms.CharField(max_length=50)
email = forms.EmailField()
content = forms.CharField()
_type = forms.ModelChoiceField(queryset=EnquiryType.objects.values_list('text', flat=True), empty_label=None)
I had the same issue.
To expand on Shang's answer:
Yes, it looks like using the primary key of the model you're referencing will work.
Here's what I did:
valid_pk = NameOfModel.objects.all()[0].pk
# You may need to replace 0 with the right number.
form_data = {'name': 'Reiss Johnson',
'email': 'reissjohnson#test.com',
'_type': str(valid_pk),
'content': 'This is some content'}
I think the problem is that you didn't understand what does ModelChoiceField do. It would generate a list of id(which is the Primary Key for that model) that corresponds to each object that the model has, so when you save, it saves the id as foreign key. You can't feed the form field with a text, which is only a field of model EnquiryType. Think about it, if you don't refer to the primary key of another table but .text, which object should the ModelChoiceField select? What if you create another object that has the same text?
The easy fix would change your test data with the id of the object that has General Enquiry as .text.
Related
I am a novice, apologies if this question seems silly. I need to save some data into MySQL database. There are no input fields. The user should click a button, and a table is updated. The data to be saved is two foreign keys and a PK.
Here is my model
class Bids(models.Model):
id=models.AutoField(primary_key=True)
userid = models.ForeignKey(Writer, on_delete=models.DO_NOTHING, related_name='userid ')
orderid = models.ForeignKey(Orders, on_delete=models.DO_NOTHING, related_name='orderids')
biddatetime=models.DateTimeField(auto_now_add=True)
I have tried writing several functions to save these fields into table bids but no joy so far. Hers's a sample.
def saveBid(request):
if request.method!= "POST":
return HttpResponse("Action Not Allowed")
else:
biddatetime=request.POST.get('biddatetime')
bids= Bids(biddatetime=biddatetime)
order=Orders(id=id)
user= CustomUser()
user.save()
bids.save()
Pls assist
I would try sending a POST request to saveBid using Postman and what error you're getting. Post the response from postman here for more help.
It could be that
biddatetime is a string and not a datetime.
On row order=Orders(id=id) you have no variable named id in your code, this will raise error.
In your model Bids the fields userid and orderid do not allow null and blank.
You can use strptime() to convert biddatetime to datetime object.
Try something like that:
from datetime import datetime
def saveBid(request):
if request.method != "POST":
return HttpResponse("Action Not Allowed")
else:
query = request.POST
# See Format Codes - link below
biddatetime = datetime.strptime(query.get('biddatetime'), "%Y-%m-%d")
# get Order
order = Orders.objects.get(id=query.get("order_id")
# create CustomUser
user = CustomUser.objects.create(username="username")
# create Bids
bids = Bids.objects.create(biddatetime=biddatetime, userid=user, orderid=order)
create() method:
create(**kwargs)
A convenience method for creating an object and saving it all in one
step. Thus:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
Linkt to Format Codes.
See also Creating objects.
Any reason why you are not using django Form or ModelForm?
class BidForm(forms.Form):
biddatetime = forms.DateTimeField()
.... // other fields
#require_POST
def saveBid(request):
form = BidForm(request.POST)
if form.is_valid():
biddatetime = form.cleaned_data.get('biddatetime')
... // do same for similar fields.
...// after user save
user.refresh_from_db() // post insert you will get the id value for the row
bids = Bids(
biddatetime=biddatetime,
userid=user.userid,
orderid=order.orderids)
bids.save()
I am assuming you are using the id value for user after save if thats not the case you can ignore it.
I have these list objects that are callable by name. They have number values that users can input. how would I turn each of those lists to form that chart js can render? I tried with this: Django Queryset to dict for use in json but could not get it working. Getting "Object of type QuerySet is not JSON serializable". Chart js would need to have own line for each of those list and show the values in those lines. This is how far I got with the link in views:
First I get all of users lists:
user_lists = List.objects.filter(user=user)
Then I get number values for each list
list_data = {}
for list in user_lists:
list_data[list.name] = DataItem.objects.filter(list=list)
Here is where I get stuck when I should convert these lists to something that chart.js can understand..
list_data_json = json.dumps(list_data, cls=DjangoJSONEncoder)
btw am I in the right tracks to put this conversion to views, right? or does it belong somewhere else?
Dont know if these are needed but here are models for these lists and the data items in them:
class List(models.Model):
name = models.CharField(max_length=100, default="")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='lists')
def __str__(self):
return self.name
class Meta:
unique_together = ['name', 'user']
class DataItem(models.Model):
data = models.IntegerField(default=0)
list = models.ForeignKey(List, on_delete=models.CASCADE, related_name='data_items')
EDIT
output query set looks like this (this is what json.dumps is trying to read i quess):
<QuerySet [{'name': 'squat', 'data_items__data': 1}, {'name': 'deadlift', 'data_items__data': 55}, {'name': 'Chest', 'data_items__data': None}, {'name': 'asd', 'data_items__data': 444}, {'name': 'asd', 'data_items__data': 32342}, {'name': 'asd', 'data_items__data': 42342}]>
And for me that looks good, there is a list of lists and list has a name "squats" and then values. But getting this error again "'QuerySet' object is not callable"
If you know what fields you want to pass to chart.js, you can do a specific values() query to get a dictionary, which you can easily serialize with json.dumps, more or less like this:
user_lists = (List.objects
.filter(user=user)
.select_related('user')
.prefetch_related('data_items')
.values('user__username', 'data_items__data') # all fields you need
)
list_data_json = json.dumps(list(user_lists))
despite what one could expect, DjangoJSONEncoder doesn't handle querysets nor models instances (see here for the types DjangoJSONEncoder deals with) - this part is actually handled by the serializer itself, but a serializer expects a Queryset and not a dict of querysets.
IOW, you will have to write your own encoder (based on DjangoJSONEncoder) to handle querysets and models (hint: someobj.__dict__ returns the object's attributes as a dict, which you can filter out to remove irrelevant django stuff like _state)
I'm implementing the schema fields by using the method get_schema_fields in DRF. On the swagger UI for the form field instead of the name which ["metrics"] I have given it is displaying the data as the name. Also the model example is also not coming.
This is the code
def get_schema_fields(self, view):
return [
coreapi.Field(
name='metrics',
location='form',
required=True,
schema=coreschema.Object(),
description='metrics type',
),
How can rename that field name from data to metrics also how to display the model sample?
I'm not sure why you want to change data to metrics, all I know is you can add or remove fields in the "Example Value" or "Model" using get_manual_fields or get_serializer_fields, here there is an example:
def get_serializer_fields(self, path, method):
fields = []
if path == 'my_path' and method == 'PUT':
fields = [coreapi.Field(
"custom_field",
required=True,
location="",
schema=coreschema.String()
)]
return fields
Here more documentation:
http://www.django-rest-framework.org/api-guide/schemas/#get_serializer_fieldsself-path-method
I am trying to access data.get_age_display in my email template. I can't seem to get the display of this. I am not sure what I am doing wrong, I've using get_FIELD_display numerous times before but passed as context to a normal template. Is there something different with forms?
class RequestForm(forms.Form):
ADULT = 1
SENIOR = 2
STUDENT = 3
AGE_GROUP = (
(ADULT, 'Adult'),
(SENIOR, 'Senior'),
(STUDENT, 'Student'),
)
name = forms.CharField(max_length=255)
phone = forms.CharField(max_length=15)
age = forms.ChoiceField(choices=AGE_GROUP)
details = forms.CharField(widget=forms.Textarea())
def save(self):
order = Order(
name = self.cleaned_data['name'],
phone = self.cleaned_data['phone'],
age = self.cleaned_data['age'],
details = self.cleaned_data['details'],
)
order.save()
template = loader.get_template('request_email.txt')
# send over the order object in an email extracted so they can handle the ticket order
context = Context({
'data': order,
})
#import pdb; pdb.set_trace()
email_subject = 'Request Tickets'
mail_managers(email_subject, template.render(context))
in my request_email.txt all I am doing is {{ data.get_age_display }} any ideas?
Jeff
You haven't shown the code for the Order model that you're creating. Are you sure that the age field on the model has choices set?
Any reason you're not using a ModelForm? You're creating an Order object within the form's save() method, but not returning it. A modelform would do that for you, as well as removing the need to redeclare the fields for the form.
I know this is coming WAAAAAY later than the question being posted but here's my answer for completeness and anyone else who might benefit from it :-)
I'm going to assume that in AGE_GROUP, ADULT, SENIOR and STUDENT are integers. Your form cleaning will NOT automatically clean the string contained in the POST and return an integer. So in this code:
context = Context({
'data': order,
})
you would think order.age is referring to an integer but that is, in fact, incorrect. It's burned me a few times before because this will correctly save the integer to the physical table, but the order instance still has the string representation of the age field.
You could do one of two things:
1. Clean this in the field:
clean_age(self):
return int(self.cleaned_data['age'])
or create a new field type:
def MyChoiceField(forms.ChoiceField):
def clean(self, value):
if not value:
if self.required:
raise forms.ValidationError(self.error_messages['required'])
return None
else:
return None
return int(value)
link that to the form field:
age = MyChoiceField(choices=AGE_GROUP)
and then you'll be able to apply this logic to any other such choice field in future. Personally, I find the latter approach the best one and I stick all my custom field types into a form_utils file so that I can use them everywhere. Another gotcha is that forms.charField doesn't automatically strip the entered text and you can use this approach to fix that too.
class Person(models.Model):
name = models.CharField(max_length=100)
class Entry(models.Model):
text = models.CharField(max_length=100)
person = models.ManyToManyField(Person, blank=True, null=True)
class MyModelForm(forms.ModelForm):
class Meta:
model = Entry
In my view, I need to add pk id's to a submitted form before saving it.
data = request.POST.copy()
# 'person' is a ManyToManyField to a 'Person' model
# a form would normally send multiple id's as POST in this format -> u'id': [u'1', u'2']
# u'1,2' (an example) is a str variable accessible to the view
data[u'person'] = u'1,2'.split(",")
form = MyModelForm(data)
if form.is_valid():
form.save()
This gives me:
int() argument must be a string or a
number, not 'list'
Which is fair enough. It does work in case of:
data[u'person'] = u'1'
I also tried this with no success:
new_form = form.save(commit=False)
new_form.person = u'1,2'.split(",")
new_form.save()
form.save_m2m()
How can I save multiple id's to this ManyToManyField?
Must be easy but I am missing the point.
EDIT:
The desired result is one new instance of MyModelForm (in the 'entry' table) with all id's stored for form.person (multiple records in the 'entry_person' table).
UPDATE:
I seem to have isolated the problem.
If I do:
data = {}
data[u'person'] = u'1,2'.split(",")
It does work as the result is:
{u'person': [u'1', u'2'],}
If I do:
data = request.POST.copy()
data[u'person'] = u'1,2'.split(",")
It does NOT work (gives the error described above) as the result is:
<QueryDict: {u'person': [[u'1', u'2']],}>
So all I need is to have
<QueryDict: {u'person': [u'1', u'2'],}>
Any suggestions how?
QueryDict.setlist(key, list_) solves this problem.
Answered in A workaround for Django QueryDict wrapping values in lists?
The split you entered returns the following:
[u'1', u'2']
To create multiple instances in your database you'd need to iterate over the list:
for x in u'1,2'.split(","):
data[u'person'] = x
form = MyModelForm(data)
if form.is_valid():
form.save()
Out of curiosity, why string to list conversion in the first place? Or is this just a general example
Try:
new_form.person = [int(x) for x in u'1,2'.split(",")]
This will set new_form.person to a list of ints, not a list of strings.