Bulk create on related models using csv - django

I'm trying to use bulk_create in order to add objects to related models. Here i'm fetching the csv file through post request which contains required fields. As of now I can add items to models which is unrelated using the csv file and bulk_create and it's working.
class BulkAPI(APIView):
def post(self, request):
paramFile = io.TextIOWrapper(request.FILES['requirementfile'].file)
dict1 = csv.DictReader(paramFile)
list_of_dict = list(dict1)
objs = [
ManpowerRequirement(
project=row['project'],
position=row['position'],
quantity=row['quantity'],
project_location=row['project_location'],
requested_date=row['requested_date'],
required_date=row['required_date'],
employment_type=row['employment_type'],
duration=row['duration'],
visa_type=row['visa_type'],
remarks=row['remarks'],
)
for row in list_of_dict
]
try:
msg = ManpowerRequirement.objects.bulk_create(objs)
returnmsg = {"status_code": 200}
print('imported successfully')
except Exception as e:
print('Error While Importing Data: ', e)
returnmsg = {"status_code": 500}
return JsonResponse(returnmsg)
My models are:
class ManpowerRequirement(models.Model):
project = models.CharField(max_length=60)
position = models.CharField(max_length=60)
quantity = models.IntegerField()
project_location = models.CharField(max_length=60)
requested_date = models.DateField()
required_date = models.DateField()
employment_type = models.CharField(max_length=60,choices = EMPLOYMENT_TYPE_CHOICES,
default = 'Permanent')
duration = models.CharField(max_length=60)
visa_type = models.CharField(max_length=60)
remarks = models.TextField(blank = True , null=True)
def __str__(self):
return self.project
class Meta:
verbose_name_plural = "Manpower_Requirement"
class Fulfillment(models.Model):
candidate_name = models.CharField(max_length=60)
manpower_requirement = models.ForeignKey(ManpowerRequirement, on_delete=models.CASCADE)
passport_number = models.CharField(blank = True, max_length=60)
subcontract_vendors = models.CharField(max_length=200,blank = True , null=True ,default='')
joined_date = models.DateField(blank = True, null = True, default = '')
remarks = models.TextField( blank = True,null = True)
def __str__(self):
return self.candidate_name
class Meta:
verbose_name_plural = "Fulfillment"
class FulfillmentStatus(models.Model):
fulfillment = models.ForeignKey(Fulfillment, on_delete=models.CASCADE)
status = models.CharField(max_length=60)
status_date = models.DateField()
remarks = models.TextField( blank = True, null = True )
def __str__(self):
return self.fulfillment.candidate_name
class Meta:
verbose_name_plural = "FulfillmentStatus"
I don't know how to do the same using bulk_create for Fulfillment and FulfillmentStatus models which are related to ManpowerRequirement. Csv file which I recieve in order to bulkcreate for Fulfillment consists of all the fields of ManpowerRequirement and all fields of Fulfillment and FulfillmentStatus excluding the foreign keys and id fields.

in the past I had the same problem; I solved in that way
Assuming that in a single CSV row you have data for all models I'd go for
create a mapping between the main model and the linked ones (you could use row index as key
use bulk_create() on the main model
iterate the dict and use bulk_create() for the linked modules
items = []
mrs = []
for row in list_of_dict:
mr = ManpowerRequirement(...)
mrs.append(mr)
f = ManpowerRequirement(...)
fs = FulfillmentStatus(...)
items.append((mr, f, fs))
# create all Manpower Requirements
ManpowerRequirements.objects.bulk_create(mrs)
a = []
for mr, f, fs in items:
f.manpower_requirement = mr
a.append(f)
# create all Fulfillments
Fulfillment.objects.bulk_create(a)
a = []
for mr, f, fs in items:
fs.fulfillment = f
a.append(fs)
# create all FulfillmentStatus
FulfillmentStatus.objects.bulk_create(a)
not sure if you can do some optimization in looping but it should solve the problem with just 3 queries

For related models we can do like this
class FulfillmentAPI(APIView):
def post(self, request):
paramFile = io.TextIOWrapper(request.FILES['fulfillmentfile'].file)
dict1 = csv.DictReader(paramFile)
list_of_dict = list(dict1)
objs = [
Fulfillment(
manpower_requirement=ManpowerRequirement.objects.get(project=row['project'], position=row['position'], quantity=row['quantity'], requested_date=row['requested_date'],),
remarks=row['remarks'],
candidate_name=row['candidate_name'],
passport_number=row['passport_number'],
joined_date=row['joined_date'],
subcontract_vendors=row['subcontract_vendors'],
)
for row in list_of_dict
]
try:
msg = Fulfillment.objects.bulk_create(objs)
returnmsg = {"status_code": 200}
print('imported successfully')
except Exception as e:
print('Error While Importing Data: ', e)
returnmsg = {"status_code": 500}
return JsonResponse(returnmsg)

Related

How can I obtain just one JSON object with the three models information combined?

How can I pass the foreign key values from my model to my serialised json object?
Now I have this three models,
class Fleet(models.Model):
fleet_id = models.IntegerField('Id flota', primary_key=True, unique=True)
fleet_name = models.CharField('Nombre flota', max_length=20, unique=True)
def __str__(self):
return self.fleet_name + ' ' + str(self.fleet_id)
class Device(models.Model):
dev_eui = models.CharField(max_length=16, primary_key=True, unique=True)
producer = models.CharField(max_length=20)
model = models.CharField(max_length=20)
dev_name = models.CharField(max_length=20, unique=True)
fleet_id = models.ForeignKey(Fleet, on_delete=models.CASCADE)
def __str__(self):
return self.dev_eui
class DevData(models.Model):
data_uuid = models.UUIDField(primary_key=True, default=uuid.uuid1, editable=False)
frequency = models.IntegerField()
data_1 = models.FloatField()
data_2 = models.FloatField(null=True, blank=True)
dev_eui = models.ForeignKey(Device, on_delete=models.CASCADE) #hay que saber porque aƱade _id
def __str__(self):
return self.dev_eui
And what I'm doing is call my view function in my JS code to obtain some data like this.
def getData(request):
ctx = {}
if request.method == 'POST':
select = int(request.POST['Select'])
data = DevData.objects.order_by('dev_eui','-data_timestamp').distinct('dev_eui')
nodes = Device.objects.all()
fleets = Fleet.objects.all()
data = loads(serializers.serialize('json', data))
nodes = loads(serializers.serialize('json', nodes))
fleets = loads(serializers.serialize('json', fleets))
ctx = {'Data':data, 'Nodes':nodes, 'Fleets':fleets}
return JsonResponse(ctx)
And inside my js file I filter it with some if else conditionals.
This works well, but I'm sure I can do it directly in my view but I don't know how. How can I obtain just one JSON object with the three models information combined?
Thank you very much!!
You can write a custom serializer like this:
from django.core.serializers.json import Serializer
class CustomSerializer(Serializer):
def end_object(self, obj):
for field in self.selected_fields:
if field == 'pk':
continue
elif field in self._current.keys():
continue
else:
try:
if '__' in field:
fields = field.split('__')
value = obj
for f in fields:
value = getattr(value, f)
if value != obj and isinstance(value, JSON_ALLOWED_OBJECTS) or value == None:
self._current[field] = value
except AttributeError:
pass
super(CustomSerializer, self).end_object(obj)
Then use it like this
serializers = CustomSerializer()
queryset = DevData.objects.all()
data = serializers.serialize(queryset, fields=('data_uuid', 'dev_eui__dev_eui', 'dev_eui__fleet_id__fleet_name'))
I have wrote an article regarding serializing nested data here. You can check that out as well.

How Serializing many-to-many through Foreign Key Django?

I have model "B" with many-to-many through Foreign Key:
class DManager(m.Manager):
def get_by_natural_key(self, name):
return self.get(name=name)
class D(m.Model):
objects = DManager()
id = m.AutoField(primary_key=True)
name = m.CharField(max_length=250, unique=True, null=False)
def natural_key(self):
return (self.name)
class A(m.Model):
id = m.IntegerField(unique=True, null=False, primary_key=True)
name = m.CharField(max_length=250, null=True)
class B(m.Model):
id = m.IntegerField(unique=True, null=False, primary_key=True)
name = m.CharField(max_length=250, null=True)
type = m.ForeignKey(D)
bs = m.ManyToManyField(A, through='C')
def natural_key(self):
## ?natural key for many-to-many?
return(self.name, self.type.natural_key(), ?????)
class C(m.Model):
a_id = m.ForeignKey(A)
b_id = m.ForeignKey(B)
I can get relation through foreign key (B-D), but I can't get relation from many-to-many (B-A) in my ajax.py:
....
if request.is_ajax():
aj_d = json.loads(request.body.decode('utf-8'))
raw_data = serializers.serialize(
'python', m.B.objects.filter(
bs__a_id__in=aj_d['data']).distinct(),
use_natural_foreign_keys=True)
output = json.dumps(raw_data)
return HttpResponse(output, content_type='application/json')
Maybe exist another way through values() for example. But I have problem with dumps list of dicts - "is not JSON serializable":
...
raw_data = m.B.objects.filter(
bs__a_id__in=aj_d['data']).distinct().values()
output = json.dumps(raw_data)
Solution:
def push_data(request):
q = m.B.objects
if request.is_ajax():
data = json.loads(request.body.decode('utf-8'))
if 'req_1' in data:
q = q.filter(bs__id__in=data['req_1'])
if 'req_2' in data:
q = q.filter(type__id__in=data['req_2'])
actual_data = q.values('name', 'id', 'type__name')
mtm_get(actual_data) ## down
return HttpResponse(json.dumps(list(actual_data)),
content_type='application/json; charset=utf8')
for many-to-many:
def mtm_get(data):
for d in data:
d['a_name'] = ', '.join(''.join(i) for i in m.B.objects.filter(
pk=d['id']).values_list('bs__name'))

"This field is required." when sending JSON using POST with Django REST Framework

I have the following JSON I'm trying to post:
[
{
"action_alert_details": [],
"action_email": [
{
"tskmail_attach": null,
"tskmail_id": 4444,
"tskmail_memo": "TEST!",
"tskmail_priority": 1,
"tskmail_subject": "TEST!",
"tskmail_to_ext": "blah#blah.com",
"tskmail_to_int": null
}
],
"action_job_details": [],
"action_log_details": [],
"action_snmp_details": [],
"action_variable_details": [],
"nodmst_name": null,
"owner_name": "Operations ",
"servicemst_name": null,
"tskmst_desc": null,
"tskmst_id": 4444,
"tskmst_lstchgtm": "2014-09-17T16:02:29",
"tskmst_name": "act_test ",
"tskmst_public": "Y",
"tskmst_type": 1
}
]
When sending it through the following view, it's complaining:
[
{
"action_email": [
{
"tskmail_id": "This field is required."
}
]
}
]
But as you can see in my JSON at the top, it's there. So, why does my view not seem to recognize that it's there during serialization?
def put(self, request, format=None):
data = request.DATA
owner = data[0]['owner_name']
ownerid = Owner.objects.filter(owner_name=owner).values_list('owner_id', flat=True)[0]
data[0].update({'owner_id': ownerid})
actid = data[0]['tskmst_id']
actname = data[0]['tskmst_name']
if data[0]['nodmst_name'] == None:
data[0].update({'nodmst_id': None})
else:
nodname = data[0]['nodmst_name']
nodid = Nodmst.objects.filter(nodmst_name=nodname).values_list('nodmst_id', flat=True)[0]
data[0].update({'nodmst_id': nodid})
if data[0]['servicemst_name'] == None:
data[0].update({'servicemst_id': None})
else:
servicename = data[0]['servicemst_name']
serviceid = Servicemst.objects.filter(servicemst_name=servicename).values_list('servicemst_id', flat=True)[0]
data[0].update({'servicemst_id': serviceid})
if Tskmst.objects.filter(tskmst_name=actname).exists():
data[0]['tskmst_id'] = Tskmst.objects.filter(tskmst_name=actname).values_list('tskmst_id', flat=True)[0]
data[0]['action_email'][0]['tskmail_id'] = Tskmst.objects.filter(tskmst_name=actname).values_list('tskmst_id', flat=True)[0]
else:
maxtskid = Tskmst.objects.latest('tskmst_id').tskmst_id
data[0]['tskmst_id'] = maxtskid + 1
data[0]['action_email'][0]['tskmail_id'] = maxtskid + 1
Tblcnt.objects.filter(tblcnt_tblname='tskmst').update(tblcnt_lstid=(maxtskid +1))
Tblcnt.objects.filter(tblcnt_tblname='tskmail').update(tblcnt_lstid=(maxtskid +1))
serializer = self.get_serializer_class()(data=request.DATA, many=True)
if serializer.is_valid():
# serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Here is my serializers that are pulling the data together:
class ActionMailSerializer(serializers.ModelSerializer):
class Meta:
model = Tskmail
resource_name = 'tskmail'
class ActionPUTSerializer(serializers.ModelSerializer):
owner_id = serializers.Field(source='owner_id')
action_email = ActionMailSerializer()
class Meta:
model = Tskmst
resource_name = 'tskmst'
depth = 1
fields = ('tskmst_id', 'tskmst_name', 'tskmst_desc', 'tskmst_type', 'owner_id', 'tskmst_public',
'tskmst_lstchgtm', 'nodmst_id', 'servicemst_id', 'action_email', 'action_alert_details',
'action_snmp_details', 'action_job_details', 'action_log_details', 'action_variable_details')
Posting this for the potential that other people might experience this issue which seems to be pertaining to it being a legacy DB. The problem lies in the fact that the PK field in "tskmail" table is also a FK to "tskmst".
See Multi-Column Primary Key support for more info.
What I ended up doing is making another set of models strictly for PUT that has no FK relationship but is strictly an Integer Field.
class TskmailPOST(models.Model):
tskmail_id = models.IntegerField(primary_key=True)
tskmail_to_int = models.TextField(blank=True)
tskmail_to_ext = models.TextField(blank=True)
tskmail_subject = models.TextField(blank=True)
tskmail_memo = models.TextField(blank=True) # This field type is a guess.
tskmail_priority = models.SmallIntegerField(blank=True, null=True)
tskmail_attach = models.TextField(blank=True)
class Meta:
managed = False
db_table = 'tskmail'
Instead of -
class TskmailPOST(models.Model):
tskmail_id = models.ForeignKey(Tskmst, db_column='tskmail_id', primary_key=True)
tskmail_to_int = models.TextField(blank=True)
tskmail_to_ext = models.TextField(blank=True)
tskmail_subject = models.TextField(blank=True)
tskmail_memo = models.TextField(blank=True) # This field type is a guess.
tskmail_priority = models.SmallIntegerField(blank=True, null=True)
tskmail_attach = models.TextField(blank=True)
class Meta:
managed = False
db_table = 'tskmail'
Then I had to call 2 separate serializers and using the POST data, break it into 2 separate dict files and validate the first, load it then validate the second and load it.

django-select2 not working with inlines in django-admin

Here are my models and admin classes:
---------------------Models-----------------------
from django.contrib.auth.models import User
class PurchaseOrder(models.Model):
buyer = models.ForeignKey(User)
is_debit = models.BooleanField(default = False)
delivery_address = models.ForeignKey('useraccounts.Address')
organisation = models.ForeignKey('useraccounts.AdminOrganisations')
date_time = models.DateTimeField(auto_now_add=True)
total_discount = models.IntegerField()
tds = models.IntegerField()
mode_of_payment = models.ForeignKey(ModeOfPayment)
is_active = models.BooleanField(default = True)
class Meta:
verbose_name_plural = "Purchase Orders"
def __unicode__(self):
return '%s' % (self.id)
----------------------------------Admin----------------------------------------
"""
This class is used to add, edit or delete the details of item purchased
"""
class PurchasedItemInline(admin.StackedInline):
form = ItemSelectForm
model = PurchasedItem
fields = ['parent_category', 'sub_category', 'item', 'qty', ]
extra = 10
class BuyerChoices(AutoModelSelect2Field):
queryset = User.objects.all()
search_fields = ['username__icontains', ]
class BuyerForm(ModelForm):
user_verbose_name = 'Buyer'
buyer = BuyerChoices(
label='Buyer',
widget=AutoHeavySelect2Widget(
select2_options={
'width': '220px',
'placeholder': 'Lookup %s ...' % user_verbose_name
}
)
)
class Meta:
model = PurchaseOrder
fields = '__all__'
"""
This class is used to add, edit or delete the details of items
purchased but buyer has not confirmed the items purchased, this class
inherits the fields of PurchaseOrder derscribing the delivery address of
buyer , is_debit , total discount , tds and mode of payment
"""
class PurchaseOrderAdmin(admin.ModelAdmin):
form = BuyerForm
#list_display = ['id','buyer','delivery_address','date_time','is_active']
inlines = [PurchasedItemInline]
# model = PurchaseOrder
#actions = [mark_active, mark_inactive]
#list_filter = ['date_time']
#search_fields = ['id']
list_per_page = 20
def response_add(self, request, obj, post_url_continue=None):
request.session['old_post'] = request.POST
request.session['purchase_order_id'] = obj.id
return HttpResponseRedirect('/suspense/add_distance/')
I am trying to implement django-select2, but when I use inlines in
PurchaseOrderAdmin it doesn't show the field where I have implemented
django-select2:
But when I remove inlines, it works fine:
Edit
Here is the ItemSelectForm
class ItemSelectForm(forms.ModelForm):
class Media:
js = (
'http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
'js/ajax.js',
)
try:
parent_category = forms.ModelChoiceField(queryset=Category.objects.\
filter(parent__parent__isnull=True).filter(parent__isnull=False))
sub_category_id = Category.objects.values_list('id',flat=True)
sub_category_name = Category.objects.values_list('name',flat=True)
sub_category_choices = [('', '--------')] + [(id, name) for id, name in
itertools.izip(sub_category_id, sub_category_name)]
sub_category = forms.ChoiceField(sub_category_choices)
except:
pass
item = forms.ModelChoiceField(queryset = Product.objects.all())
def __init__(self, *args, **kwargs):
super(ItemSelectForm, self).__init__(*args, **kwargs)
self.fields['parent_category'].widget.attrs={'class': 'parent_category'}
self.fields['sub_category'].widget.attrs={'class': 'sub_category'}
self.fields['item'].widget.attrs={'class': 'item'}
It worked for me by adding the following line in the static/suit/js/suit.js
Add:
(function ($) {
Suit.after_inline.register('init_select2', function(inline_prefix, row){
$(row).find('select').select2();
});

Django - How to do a transaction when saving a form?

I'm saving a form, but there is one save() method that is outside the transaction. The save() method outside a transaction is the save() on the "BicycleAdCategoryForm".
Here is the code:
models.py
class Main(models.Model):
section = models.ForeignKey(Section)
user = models.ForeignKey(User)
title = models.CharField(max_length=250)
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.title
# To order in the admin by name of the section
class Meta:
ordering = ['date_inserted']
class BicycleAd(models.Model):
main = models.ForeignKey(Main)
bicycleadtype = models.ForeignKey(BicycleAdType)
bicycleaditemkind = models.ForeignKey(BicycleAdItemKind) # MPTT Model
bicycleadcondition = models.ForeignKey(BicycleAdCondition)
country = models.ForeignKey(GeonamesCountry)
city = models.ForeignKey(GeonamesLocal)
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
# To order in the admin by name of the section
class Meta:
ordering = ['date_inserted']
class BicycleAdCategoryType(models.Model):
n_bicycle_ad_category_type = models.CharField(max_length=100) # COMPRA, VENDA, TROCA
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.n_bicycle_ad_category_type
# To order in the admin by name of the section
class Meta:
ordering = ['n_bicycle_ad_category_type']
forms.py
class MainForm(forms.ModelForm):
class Meta:
model = Main
exclude = ('user', 'section')
class BicycleAdForm(forms.ModelForm):
class Meta:
model = BicycleAd
exclude = ('main', 'bicycleadtype', 'bicycleaditemkind', 'bicycleadcondition', 'city') # DPS RETIRAR DAQUI A "CITY"
class BicycleAdCategoryForm(forms.ModelForm):
bicycleadcategorytype = forms.ModelMultipleChoiceField(queryset=BicycleAdCategoryType.objects.all(), required=False, widget=forms.CheckboxSelectMultiple) # Se retirar o widget fico uma SELECT box em q posso selecionar varias opcoes
class Meta:
model = BicycleAdCategory
exclude = ('bicyclead',)
def save(self, commit, rel_obj):
data = self.data.getlist('bicycleadcategorytype')
for item in data:
obj_bicycleadcategory = BicycleAdCategory()
obj_bicycleadcategory.bicyclead = rel_obj
obj_bicycleadcategory.bicycleadcategorytype = BicycleAdCategoryType.objects.get(pk=item)
obj_bicycleadcategory.save()
def clean_bicycleadcategorytype(self):
data = self.cleaned_data['bicycleadcategorytype']
try:
for item in data:
bicycleadcategorytype = BicycleAdCategoryType.objects.get(pk=item.pk)
return bicycleadcategorytype
except (KeyError, BicycleAdCategoryType.DoesNotExist):
raise forms.ValidationError('Invalid Bicycle Ad Category Type. Please try again.')
views.py
def submit_ad_view(request):
if request.method == 'POST':
model_main = Main()
model_main.section = Section.objects.get(pk=request.POST['section'])
model_main.user = request.user
model_bicyclead = BicycleAd()
model_bicyclead.bicycleadtype = BicycleAdType.objects.get(pk=2)
model_bicyclead.bicycleaditemkind = BicycleAdItemKind.objects.get(pk=4)
model_bicyclead.bicycleadcondition = BicycleAdCondition.objects.get(pk=2)
model_bicyclead.city = GeonamesLocal.objects.get(pk=4803854)
form_main = MainForm(request.POST, instance = model_main)
form_bicyclead = BicycleAdForm(request.POST, instance = model_bicyclead)
form_bicycleadcategory = BicycleAdCategoryForm(request.POST)
if form_main.is_valid() and form_bicyclead.is_valid() and form_bicycleadcategory.is_valid():
main_f = form_main.save()
bicyclead_f = form_bicyclead.save(commit=False)
bicyclead_f.main = main_f
bicyclead_f.save()
bicycleadcategory_f = form_bicycleadcategory.save(commit=False, rel_obj=model_bicyclead)
resultado = 'valid'
else:
resultado = 'n_valid'
return render_to_response('app/submit_ad.html', {'resultado': resultado}, context_instance=RequestContext(request))
I think main_f and bicyclead_f are inside a transaction but bicycleadcategory_f is outside a transaction. When bicycleadcategory_f fails, main_f and bicyclead_f are stored in the database.
Any clue on what I'm doing wrong?
Best Regards,
Django executes views using the commit_on_success decorator (or at least it behaves that way). If you're view crashes (uncaught exceptions), a rollback should take place. If some data is stored, and some is not there is a possibility that your DB engine does not support transactional processing.
Check out the django doc for more info
https://docs.djangoproject.com/en/dev/ref/databases/
For example, if you're using MySQL with MyISAM you may encounter some problems
edit:
Krzysiek Szularz: I guess everybody is using django TransactionMiddleware or simmilar things, so I skipped it - and mentioned only the logic layer.