Django bulk create for non-repetitive entries - django

I want to insert data from an Excel file to the database, but I want to insert only non-repetitive ones.
I wrote this code, but the if statement is always False!
def ca_import(request):
uploadform=UploadFileForm(None)
if request.method == 'POST' :
uploadform = UploadFileForm(request.POST, request.FILES)
if uploadform.is_valid():
file = uploadform.cleaned_data['docfile']
workbook = openpyxl.load_workbook(filename=file, read_only=True)
# Get name of the first sheet and then open sheet by name
first_sheet = workbook.get_sheet_names()[0]
worksheet = workbook.get_sheet_by_name(first_sheet)
data = []
try:
for row in worksheet.iter_rows(row_offset=1): # Offset for header
stockname =StocksName()
if (StocksName.objects.filter(name=row[0].value).count()<1): #???
stockname.name=row[0].value
data.append(stockname)
StocksName.objects.bulk_create(data)
messages.success(request,"Successful" ,extra_tags="saveexcel")
except :
messages.error(request,_('Error'),extra_tags="excelerror")
return render(request, 'BallbearingSite/excelfile.html',{'uploadform':uploadform})
Any suggestion to solve it?

If you data has a unique id then you can use get_or_create() or update_or_create() instead of bulk_create()
Otherwise you will have to write the logic to check if each line already exists in your model.

Related

"form.populate_by returns" ERROR:'list' object has no attribute

I am creating a view function to edit the database using a wtform, I want to populate the form with information held on the database supplied by a differente form, My problem is the query that provides the details
I have read the manual https://wtforms.readthedocs.io/en/stable/crash_course.html
and the following question Python Flask-WTF - use same form template for add and edit operations
but my query does not seem to supply the correct format of data
datatbase model:
class Sensors(db.Model):
id = db.Column(db.Integer, primary_key=True)
sensorID = db.Column(db.String, unique=True)
name = db.Column(db.String(30), unique=True)
form model:
class AddSensorForm(FlaskForm):
sensorID = StringField('sensorID', validators=[DataRequired()])
sensorName = StringField('sensorName', validators=[DataRequired()])
submit = SubmitField('Register')
view function:
#bp.route('/sensors/editsensor/<int:id>', methods=('GET', 'POST'))
#login_required
def editsensor(id):
edit = [(s.sensorID, s.sensorName) for s in db.session.\
query(Sensors).filter_by(id=id).all()]
form = AddSensorForm(obj=edit)
form.populate_obj(edit)
if form.validate_on_submit():
sensors = Sensors(sensorID=form.sensorID.data, sensorName=form.sensorNa$
db.session.add(sensors)
db.session.commit()
shell code for query:
from homeHeating import db
from homeHeating import create_app
app = create_app()
app.app_context().push()
def editsensor(id):
edit = [(s.sensorID, s.sensorName) for s in db.session.query(Sensors).filter_by(id=id).all()]
print(edit)
editsensor(1)
[('28-0000045680fde', 'Boiler input')]
I expect that the two form fields will be populated with the in formation concerning the sensor called by its 'id'
but I get this error
File "/home/pi/heating/homeHeating/sensors/sensors.py", line 60, in
editsensor
form.populate_obj(edit)
File "/home/pi/heating/venv/lib/python3.7/site-
packages/wtforms/form.py", line 96, in populate_obj
Open an interactive python shell in this
framefield.populate_obj(obj, name)
File "/home/pi/heating/venv/lib/python3.7/site-
packages/wtforms/fields/core.py", line 330, in populate_obj
setattr(obj, name, self.data)
AttributeError: 'list' object has no attribute 'sensorID'
The error indicates that it wants 2 parts for each field "framefield.populate_obj(obj, name) mine provides only one the column data but not the column name, "sensorID"
If i hash # out the line "edit = ..." then there are no error messages and the form is returned but the fields are empty. So I want the form to be returned with the information in the database, filled in so that i can modify the name or the sensorID and then update the database.
I hope that this is clear
Warm regards
paul.
ps I have followed the instruction so the ERROR statement is only the part after "field.populate_by".
You are trying to pass a 1-item list to your form.
Typically, when you are selecting a single record based on the primary key of your model, use Query.get() instead of Query.filter(...).all()[0].
Furthermore, you need to pass the request data to your form to validate it on submit, and also to pre-fill the fields when the form reports errors.
Form.validate_on_submit will be return True only if your request method is POST and your form passes validation; it is the step where your form tells you "the user provided syntactically correct information, now you may do more checks and I may populate an existing object with the data provided to me".
You also need to handle cases where the form is being displayed to the user for the first time.
#bp.route('/sensors/editsensor/<int:id>', methods=('GET', 'POST'))
#login_required
def editsensor(id):
obj = Sensors.query.get(id) or Sensors()
form = AddSensorForm(request.form, obj=obj)
if form.validate_on_submit():
form.populate_obj(obj)
db.session.add(obj)
db.session.commit()
# return response or redirect here
return redirect(...)
else:
# either the form has errors, or the user is displaying it for
# the first time (GET)
return render_template('sensors.html', form=form, obj=obj)

django model and csv file zip feature lack

I fixed a lot with your help and I guess we come to last problem;
If the line from the csv file not in the my django model database than django mixes up all so that; csv line and django database are no longer going in correct order so all mixed.
To prevent this problem I added if queryset count function within the loop to raise an error message or tried also pushing another default value but nothing worked. What you would suggest to prevent this sync problem ?
for instance in RFP.objects.filter(FP_Item=query):
if RFP.objects.filter(FP_Item=query).count() >= 1:
instances.append(instance)
else:
messages.success(request, "ERROR")
For reference the whole code:
with open(path, encoding='utf-8') as f:
data = csv.reader(f, delimiter='|')
for row in data:
line = row[0]
lines.append(line)
query = line
for instance in FP.objects.filter(FP_Item=query):
if FP.objects.filter(FP_Item=query).count() <= 1:
instances.append(instance)
else:
messages.success(request, "ERROR")
pair = zip(lines, instances)
context = {'pair': pair,
}
return render(request, 'check_fp.html', context)
You can't use filter queryset and for if else loops.
You need to use get function with try and except to catch empty instance.
try:
instance = FP.objects.get(FP_Item=query)
instances.append(instance)
except FP.DoesNotExist:
instance = ["Check"]
instances.append(instance)
pair = zip(lines, instances)

How to cast Django form to dict where keys are field id in template and values are initial values?

I have a huge django form and I need to create dict where keys are field id in template and values are initial values?
Something like this:
{'field1_id_in_template': value1, ...}
Does somebody know how do that?
I can add prefix 'id_' for each field name in form.fields dictionary but I can have a problem if somebody change id for widget.attrs
This is method of CBV:
def post_ajax(self, request, *args, **kwargs):
form = ChooseForm(request.POST, log=log)
if form.is_valid():
instance = form.save()
inst_form = InstanceForm(instance=instance, account=request.user)
fields = {}
for name in inst_form.fields:
if name in inst_form.initial:
fields[inst_form.auto_id % name] = inst_form.initial[name]
return HttpResponse(
json.dumps({'status': 'OK','fields':fields},
mimetype='appplication/json'
)
assert False
And this a reason why I do that:
With this response I can write something like this on client. Now I don't need to manualy initialize all fields on the page
function mergeFields(data) {
for(var id in data) {
$("#"+id).val(data[id]).change();
}
}
Originally added to the question body itself reference.
If you have the auto_id set to True then you can get the id with form_object_instance.field_name.auto_id. With that in mind you can create your dict by iterating over the form object.
I am just wondering why you would need to do such a processing as the form object is usually used to encapsulate such behaviors...
you can try getattr.
For example, you have a known key list as
['field1_id_in_template', 'field2_id_in_template', ...]
Then:
my_values = dict()
for key in key_list:
value = getattr(your_form, key)
# now do something with it
my_values[key] = deal_with(value)

Set default value for dynamic choice field

I have a form that asks the user to enter in their zip code. Once they do it sends them to another form where there is a field called 'pickup_date'. This gets the value of the zip from the previous field and gets all of the available pickup_dates that match that zip code into a ChoiceField. I set all of this within the init of the model form.
def __init__(self,*args,**kwargs):
super(ExternalDonateForm,self).__init__(*args,**kwargs)
if kwargs:
zip = kwargs['initial']['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
elif self.errors:
zip = self.data['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
The problem I have is when there are other errors on the form. I use the elif self.errors to regenerate the possible choices but it doesn't default to the original selected option. It goes back and defaults to the first choice. How can I make it so it's default option on form errors is what was originally posted?
Change self.fields['pickup_date'] to self.fields['pickup_date'].initial and see if that helps.
I got it to work after playing around for a while. Above, I was setting all the dynamic choices with a get_dates() function that returned a tuple. Instead of doing that I returned a field object like this using a customized ModelChoiceField instead of a regular ChoiceField....
class MyModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.date.strftime('%a %b %d, %Y')
Dates function
def get_dates(self,zip):
routes = Route.objects.filter(zip=zip).values_list('route',flat=True)
pickups = self.MyModelChoiceField(queryset = PickupSchedule.objects.filter(
current_count__lt=F('specials'),
route__in=routes,
).order_by('date')
)
if not pickups:
pickups = (('----','No Pickups Available At This Time'),)
return pickups
in the init i set the value for self.fields['pickup_date'] like so..
self.fields['pickup_date'] = self.get_dates(zip)

submitting form results into db - django

i created a form to save a post into db for my blog project. I've designed index page. now i am tryin to create a form to create new posts. before that i was using ' manage.py shell'
here is my view :
def addpost(request):
form = addForm()
if request.method=="POST":
titleform = request.POST['title']
bodyform = request.POST['body']
checkform = request.POST['isdraft']
if form.is_valid():
n = Post(title = titleform, body = bodyform, isdraft=checkform)
n.save()
return HttpResponseRedirect('/admin/')
else:
pass
return render(request,'userside/add.html',{'form':form,})
my model.py:
class Post(models.Model):
title = models.CharField(max_length = 100)
body = models.TextField()
slug = AutoSlugField(populate_from='title',unique=True)
posted = models.DateField(auto_now_add=True)
isdraft = models.BooleanField()
def __unicode__(self):
return self.title
#permalink
def get_absolute_url(self):
return ('view_blog_post',None, {'postslug':self.slug})
class addForm(forms.Form):
title = forms.CharField(max_length=100)
body = forms.CharField(widget=forms.Textarea)
isdraft = forms.BooleanField()
if i submit form as 'isdraft' field is False(unchecked) ; it gives error like:
MultiValueDictKeyError at /admin/addpost/
"Key 'isdraft' not found in "
and if i submit the form as 'isdraft' field is True(checked) ; it gives nothing. just refreshing form. no adding data into db.
i am doing sth wrong..
thank you
edit : Dmitry Beransky's answer worked for checkbox error. but it still doesnt add any data into db. just refreshes the form.
The whole point of using a form is that it takes care of validation and cleaning, that is converting values to the proper data types. That's why you should be accessing form.cleaned_data rather than reques.POST, and you should be doing it inside the if form.is_valid() check.
Edit
I've just noticed that you're never passing request.POST to the form. So form.is_valid() will never be true.
Please go back and read the documentation about using a form in a view.
If a checkbox is not checked in your HTML form, it's name/value is not going to be included in the data that the browser sends to your server. Which meanst that the request.POST dictionary is not going to contain an entry for 'isdraft' which in turn will cause a key error when you try to read the isdraft value. A solution is to change the way you read the value from the posted data to:
checkform = request.POST.get('isdraft', False)
rather than throw an error if isdraft isn't found in the dictionary, this will set checkform to False (the default value in case of a missing key)
Maybe your form does not validate at all. Have you checked if your code even reaches those lines after the if form.is_valid() statement ? If they do, what you've done there is right and should create the db row for your new entry, though you could have used
Post.objects.create(....) , and that would have taken away the need for calling the method save().
Some points though:
instead of checking for request.POST , check for request.method == 'POST' , cause there might be a post which has an empty POST dict ( in case no arguments have been submitted ), in that case request.POST fails to provide the right check .
see the docs for more info : request.POST
instead of using request.POST['var_name'] , use request.POST.get('var_name', 'default_value') , cause doing this like request.POST['var_name'] might result in some exceptions ( in case for example the argument is not provided , like what happened for your checkform variable )
Try accessing those variables through form.cleaned_data
and finally , you don't need the else statement in the end , just use the indentation :)