How to pass non-form data in django form object? - django

I have a data dictionary and want to validate and clean that data to store in database table as defined in models.py
My approach was creating a form class then create an instance of that form class and pass data dictionary instead of request.POST in the view.py.
But this is not working. No error, but cleaned_data returning an empty dictionary.
data = {
'story_title': i.title.text,
'story_source': Sources.objects.get(source_url= source_url),
'pub_date': i.pubDate.text[5:16],
'body_text': des,
'url': i.link.text
}
story_form = StoryForm(data)
if story_form.is_valid():
story_data = story_form.cleaned_data
new_story = Stories(
story_title = story_data['story_title'],
story_source = story_data['story_source'],
pub_date = story_data['pub_date'],
body_text = story_data['body_text'],
url = story_data['url']
)
new_story.save()
I am not confirm that my approach is right or wrong.
While searching for solution I came across HttpRequest.body in Django docs which only says that HttpRequest.body is used to populate form instance with the non-form data / raw data (nothing about how to use).

Related

Python Marshmallow & SQL-Alchemy - update/store complex objects

Hello community!
So I a a student and relatively new to python, implementing a CRUD-API right now, based on SQL-Alchemy, restx and Flask. For the conversion of the database-objects, I use Marshmallow. The database is a legacy database which can not be changed. In the database, there are some attributes that are encoded, for example a list of labs is encoded as string with the lab names in it. I use converters to cover them, as shown here:
gene_bp = Blueprint('gene', __name__)
api = createApi(gene_bp)
class FooDto(SQLAlchemyAutoSchema):
class Meta:
model = Foo
include_fk = True
include_relationships = True
load_instance = True
bar = decimal_converter.DecimalEncoder()
The Endpoint does look like this:
#api.route('/api/<string:id>')
#api.doc()
class FooRoute(Resource):
def get(self, id):
foo = get_foo_or_abort(id)
if foo:
return createSuccessResponse(FooDto().dump(foo))
The reading-part of the API is no problem with this, but now I struggle with the write/update part. This is my code until now:
#api.route('/api/<string:id>')
#api.doc()
class FooRoute(Resource):
#api.doc(params={'dto': 'Item to update', 'id': 'object id'})
def put(self, id):
fooToStore: FooDto = api.payload
if not fooToStore:
abort(400)
if fooToStore['id'] != id:
abort(400)
# todo: payload to database object
# todo: save payload to database
db.session.commit()
return updatedFoo
So as you can see, there are two ToDos open:
How do i convert the DTO back to a database object, without manual mapping? I understand that there is this method
fooToUpdate = dto.load(geneToStore)
but how do I add the needed complexity for encoded attributes like the lab list, or foreign keys to it?
What is the best practice in storing the data to the database?
Thank you all very much!

Django - Saving data without using input fields/forms

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.

overriding django formset initial data dynamically

Ok im new to django
So ive got a situation where i want a formset to have dynamic initial data
So basically here is what im looking for.
each form in the formset to have a different UserID
and a set of groups permission which they can choose from based from the initial data
here is my form
class assignGroupPermissionToUser(forms.ModelForm):
UserID = forms.ModelChoiceField(queryset=None)
Groups = forms.ModelMultipleCHoiceField(queryset=None, widget=FilteredSelectMultiple("Groups")
class Meta:
model=User
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
Userid = kwargs.pop("UserID")
self.fields['UserID'].queryset =User.objects.get(UserID=Userid)
Permissions = kwargs.pop("Groups")
listofPermission = None
for each perm in permission:
listofPermission |= Permissions.objects.filter(GroupID=perm)
self.fields['Groups'].queryset = listofPermission
the data i wanna pass is built into a list like so
it is called
completeList
> completeList =[['13452',{'group1':'Admin','group2':'FrontDesk'}],['3532','group1':'Supervisors','group2':'ReadOnly;}]]
where the first value in each nested loop is the UserID, and the dictionary is the groups they can choose from.
override method in View.py
....
form = assignGroupPermissionToUser()
assignment = formset_factory(form,extra=0)
formset = [ assignment.__init__(completeList[x][0],completeList[x][1]) for x in range(len(completeList))]
then i get an error that str object has no 'is_bound' field line 58 of formset.py
im trytin to get this data to show up on each form and based on the user
it will be all different but everything i try to override it fails for initial form so here i am stuck
note that the Group attribute in the modelform has a widget which is used in the admin section to filter from multiple choices.
settings
Django= 1.8
python 3.5
i erased all this code and just did two loops like so
formset = assignments(initial=[{'UserID': listofUserID[x] } for x in range(len(completeList))])
#then
for form in formset:
form.fields['permissions'].queryset = querysetiwant

Django validate data when updating model with primary key

I am having trouble with updating fields of a model instance. The model is as follows:
class commonInfo(models.Model):
mothers_id = models.IntegerField(primary_key=True)
date = models.DateField()
data_collector = models.CharField(max_length=50)
Essentially, I just want to do this, but it won't work because commonInfo has a user defined primary key
commonInfo_form(request.POST or None).is_valid()
Since I am updating, I am overriding date and data_collector, but not mothers_id. So I would want to do something like this, but this specific code is not working
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST)
date = form.cleaned_data['data_collector'] #this line is not working
data_collector = form.cleaned_data['data_collector'] #this line is not working
obj.update(**{'date':date, 'data_collector':data_collector})
any ideas? I feel like it is just those two lines that I need to fix. Or if there is a more pythonic way or built method in Django?
Just validate with isinstance. so like,
if isinstance(request.POST['date'], datetime.date) and isinstance(request.POST['data_collector'], str):
# you might have to use getattr for request.POST here, I'm not sure
# and request.POST['date'] would have to be converted from a string to datetime.date I think
date = request.POST['date']
data_collector = request.POST['data_collector']
obj.update(**{'date':date, 'data_collector':data_collector})
The process for adding a record from a form is different from updating an existing instance. All you need to do differently is indicate which instance to bind the form to when you create it, ex:
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST, instance=obj)

Django set form initial data in view

I'm trying to populate a django modelform with initial data provided from an external source. To achieve that I start by pull all the needed data from the external source:
url =('http://myapi.example.com')
data = urllib2.urlopen(url)
result = json.load(data)
api_data_name = result['properties']['name']
api_data_type = result['properties']['type']
Followed by populating a dict which will serve as initial data to my form:
data = {}
for field in my_model._meta.fields:
if field.name == 'name':
data[field.name] = api_data_name
form = MyEditForm(initial=data)
Then I'm passing the form to the template and the initial data is populating my text fields as expected, but now I need to be able to set a value of a select field based on a string I receive from my external source and Im not getting how can I achieve that, since doing something like:
if field.name == 'type':
data[field.name] = api_data_type
Wont do the job cause the select element has "0", "1", "2", etc as options value and not the long description i get from api_data_type variable.
How can I get the long_description from all the options <option value="1">long_description</option> of my select field in my view so i can compare each one with api_data_type?
Heres a sample of my models.py and forms.py:
#models.py
TYPE = (
('0',_(u'Type1')),
('1',_(u'Type2')),
('2',_(u'Type3')),
)
class MyModel(models.Model):
...
type=models.CharField(max_length=30,choices=TYPE,blank=True)
...
#forms.py
class MyEditForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
...
'type': Select(attrs={'class':'select-small span2'}),
...
}
Found out how to accomplish what I asked.
# For select fields
if field.name == 'classification':
for choice in field.choices:
if choice[1].lower() == api_poi_classification.lower():
data[field.name] = choice[0]
And for any of ya trying to populate many-to-many fields (as checkboxes in my case)
# Many to many fields populate
for field in hotel_poi._meta.many_to_many:
if field.name == 'views':
if u'Vista' in api_poi_review_fields:
api_vistas = api_poi_review[u'Vista']
# The api_vistas string comes from api in the format (v1; v2; v3; v4)
views = api_vistas.split(';')
choices = field.get_choices()
temp = []
for view in views:
for choice in choices:
if view.lower().strip() == choice[1].lower().strip():
temp.append(choice[0])
data[field.name]=temp
All of this could be avoided if I had direct database access... In that case i would just need to set an object instance like m = MyModel.objects.filter(id=1) and call form = MyEditForm(instance=m)
But that was not the case and that's what makes this question a bit particular.