I'm using 'django import export' (DIE) for importing and updating some data.
Import process starts from checking exists objects in DB, searching by values in ID-field, and if row with ID from import file not found - new entre will be created. How can i made "update only" scenario, where if 'id key' not found in DB, row will be skipped (not added new)?
my model.py
class Size(models.Model):
id = models.AutoField(unique=True, primary_key=True, null=False, blank=False)
height = models.SmallIntegerField()
width = models.SmallIntegerField()
class Product(models.Model):
id = models.AutoField(unique=True, primary_key=True, null=False, blank=False)
vendor_code = models.CharField(unique=True, max_length=50, null=False, blank=False)
price = models.DecimalField(null=False, blank=False)
size = models.ForeignKey(Size, verbose_name=u'Size')
in resource.py
class ProductSyncResource(resources.ModelResource):
class Meta:
model = ProductVariant
import_id_fields = ('vendor_code',)
fields = ('vendor_code', 'price',)
export_order = ('vendor_code', 'price', 'status', )
skip_unchanged = True
report_skipped = True
dry_run = True
import table (xls)
If vendor_code 'Tк-12856' (Cell A3) will be not found, then DIE will try to add this row, and:
We will get error from DB (foreignKey check for column 'size')
I don't need to add this row to DB in my 'update scenario'
Finally i got it by overriding skip_row. Fields now can be 'null=False' and will be imported only rows with known import_id_field values.
class VariantSyncResource(resources.ModelResource):
class Meta:
model = ProductVariant
import_id_field = 'vendor_code'
import_id_fields = ('vendor_code',)
fields = ('vendor_code', 'price', 'status', )
export_order = ('vendor_code', 'price', 'status', )
skip_unchanged = True
report_skipped = False
dry_run = True
def skip_row(self, instance, original):
original_id_value = getattr(original, self._meta.import_id_field)
instance_id_value = getattr(instance, self._meta.import_id_field)
if original_id_value != instance_id_value:
return True
if not self._meta.skip_unchanged:
return False
for field in self.get_fields():
try:
if list(field.get_value(instance).all()) != list(field.get_value(original).all()):
return False
except AttributeError:
if field.get_value(instance) != field.get_value(original):
return False
return True
If you want it to not create new objects I think you'd need to override the import_row() within ProductSyncResource.
Then you could just do if new: return;
def import_row(self, row, instance_loader, using_transactions=True, dry_run=False, **kwargs):
row_result = self.get_row_result_class()()
try:
self.before_import_row(row, **kwargs)
instance, new = self.get_or_init_instance(instance_loader, row)
self.after_import_instance(instance, new, **kwargs)
if new:
return row_result
else:
row_result.import_type = RowResult.IMPORT_TYPE_UPDATE
row_result.new_record = new
original = deepcopy(instance)
...
Following your comment about the preview error, just remember that this isn't an official feature of the app so you'll have to just follow stack traces & work around issues that popup. My first thought above looked like the quickest & easiest way to do this, but you could try to make the else for the save save conditional against new objects;
elif not new:
with transaction.atomic():
self.save_instance(instance, using_transactions, dry_run)
self.save_m2m(instance, row, using_transactions, dry_run)
diff.compare_with(self, instance, dry_run)
Related
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)
I want to set the value of is_delete field True when i click delete button from list page.
This is my views.py file
def delete_faculty(request, faculty_id):
faculty = Faculty.objects.get(id=faculty_id)
faculty.is_delete = True
return redirect('faculty-list')
This is models.py
class Faculty(models.Model):
name = models.CharField(max_length=255, unique=True)
established = models.DateField()
about = models.TextField()
status_choice = (
('active', 'Active'),
('disabled', 'Disabled'),
('paused', 'Paused')
)
status = models.CharField(choices=status_choice, max_length=15)
is_delete = models.BooleanField(default=False)
def __str__(self):
return self.name
This code can not set the value of True in is_delete field . How can i solve this?
# You just need to save the data
def delete_faculty(request, faculty_id):
faculty = Faculty.objects.get(id=faculty_id)
faculty.is_delete = True
faculty.save()
return redirect('faculty-list')
model 1
class Products(models.Model):
product_category = models.ForeignKey(ProductCategory)
product_sub_category = models.ForeignKey(ProductCategory)
product_name = models.CharField(max_length = 200)
is_active = models.BooleanField(default = True)
and so on...
model 2
class ProductImages(models.Model):
product = models.ForeignKey( Products )
product_image = models.FileField(_('Attachment'), upload_to='attachments')
is_active = models.BooleanField(default = True)
CreateView
class ProductCreate(CreateView):
model = Products
template_name = "products/product_add.html"
fields = ['product_category', 'product_sub_category', 'product_name', 'size', 'color', 'price', 'price_info', 'description_1', 'description_2', 'about_product', 'features', 'specification']
success_url = "products/product-list"
def form_valid(self, form):
product_form = form.save(commit = False)
# **expecting product_form.id to be non None**
if 'product_images' in self.request.FILES:
for img in self.request.FILES.getlist('product_images'):
ProductImages(product = product_form, product_image = img).save()
super(ProductCreate, self).post(request, *args, **kwargs)
Problem statement :
form.save( commit = False ) returning Product instance but product id is None. Is it illegal to expect object id as object is not yet saved to db?
Yes. The id is allocated by the database, and since commut=False explicitly means "don't send to the db", it won't have an id.
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();
});
I am getting this at every attempt.
Cannot assign "u''": "Company.parent" must be a "Company" instance.
I do not know what else to do.
The view code is still half baked, sorry for that.
Am I passing wrong parameters to the form?
I have the following model:
models.py
class Company(AL_Node):
parent = models.ForeignKey('self',
related_name='children_set',
null=True,
db_index=True)
node_order_by = ['id', 'company_name']
id = models.AutoField(primary_key=True)
company_name = models.CharField(max_length=100L, db_column='company_name') # Field name made lowercase.
next_billing_date = models.DateTimeField()
last_billing_date = models.DateTimeField(null=True)
weekly = 'we'
twice_a_month = '2m'
every_two_weeks = '2w'
monthly = 'mo'
billing_period_choices = (
(weekly, 'Weekly'),
(every_two_weeks, 'Every two weeks'),
(twice_a_month, 'Every two weeks'),
(monthly, 'Monthly'),
)
billing_period = models.CharField(max_length=2,
choices=billing_period_choices,
default=weekly)
objects = CompanyManager()
The following forms.py:
class newCompany(ModelForm):
company_name = forms.CharField(label='Company Name',
widget=forms.TextInput(attrs={'class': 'oversize expand input-text'}))
billing_period = forms.ModelChoiceField
next_billing_date = forms.CharField(widget=forms.TextInput(attrs={'class': 'input-text small', 'id': 'datepicker'}))
parent = forms.CharField(widget=forms.HiddenInput(), required=False)
class Meta:
model = Company
fields = ["company_name", "parent", "billing_period", "next_billing_date"]
The following view:
def create_company(request):
userid = User.objects.get(username=request.user).id
my_company_id = CompanyUsers.objects.get(user_id=userid).company_id
my_company_name = Company.objects.get(id=my_company_id).company_name
machines = Title.objects.raw(
'select machines.id, title.name, machines.moneyin, machines.moneyout, moneyin - moneyout as profit, machines.lastmoneyinoutupdate, (select auth_user.username from auth_user where machines.operator = auth_user.id) as operator, (select auth_user.username from auth_user where machines.readers = auth_user.id) as readers from machines, title where machines.title = title.id and machines.company_id =%s',
[my_company_id])
if request.method == 'POST':
form_company = newCompany(request.POST)
if form_company.is_valid():
new_company = form_company.save(commit=False)
new_company.parent = my_company_id
if request.POST.get('select_machine'):
selected_machine = request.POST.getlist('select_machine')
percentage = request.POST.get('percentage')
if not Beneficiary.objects.check_assign_machine(my_company_id, selected_machine, percentage):
target_company_name = new_company.company_name
target_company_id = Company.objects.get(company_name=target_company_name).id
new_company.save()
Machines.objects.assign_machine(target_company_id, selected_machine)
Beneficiary.objects.create_beneficiary(percentage, target_company_name, my_company_id, selected_machine)
else:
invalid_machines = Beneficiary.objects.check_assign_machine(my_company_id, selected_machine, percentage)
return render(request, 'lhmes/createcompany.html',
{'form_company': form_company, 'machines': machines, 'my_company_name': my_company_name, 'invalid_machines' : invalid_machines})
else:
new_company.save()
else:
form_company = newCompany()
return render(request, 'lhmes/createcompany.html',
{'form_company': form_company, 'machines': machines, 'my_company_name': my_company_name})
The error message says you are trying to set a relationship with a string but Django expects the value to be an instance of the Company model. You should assign the foreign key fields with a real model instance instead of only the primary key.
I've spotted a few places in the code where you are assigning a PK:
new_company.parent = my_company_id
Where the model expects it to be an instance:
new_company.parent = Company.objects.get(id=my_company_id)
I really don't remember if this works, but you can try:
new_company.parent_id = int(my_company_id)
This would spare a trip to the database.