The documentation for django-import-export is a bit weak on how to configure the admin to import from a spreadsheet. Does anyone have a full example?
This is not a fully complete module. But you can understand how it should be.
resources.py file
from import_export import resources
from .models import edxUser
class edxUserResource(resources.ModelResource):
class Meta:
model = edxUser
#skip_unchanged = True
#report_skipped = True
#if you want to exclude any field from exporting
exclude = ('id','edx_anonymized_id')
fields = ('id', 'edx_id', 'edx_anonymized_id', 'edx_email', 'edx_name', 'time_created', 'created_by')
#Order of the export fields
export_order = ('edx_id', 'edx_email')
admin.py file
from import_export.admin import ImportExportModelAdmin
from django.contrib import admin
from .models import edxUser
from resources import edxUserResource
#admin.register(edxUser)
class edxUserAdmin(ImportExportModelAdmin):
resource_class = edxUserResource
models.py file
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
class edxUser(models.Model):
edx_id = models.IntegerField('edX user id', blank=True, null=True)
edx_anonymized_id = models.IntegerField("edX anonymized user id", blank=True, null=True)
edx_email = models.EmailField('edx user email', max_length=75, blank=True)
edx_name = models.CharField('edx name', max_length=75, blank=True, null=True)
time_created = models.DateField('Created time', blank=True, null=True)
created_by = models.OneToOneField(settings.AUTH_USER_MODEL, null=True, blank=True)
def __unicode__(self):
return str(self.edx_id)
Here's how to do it, assuming that the column names in the spreadsheet are Title and Field one. This example assumes that the model instances will be created afresh every import (rather than being updated via a primary key).
from django.contrib import admin
from import_export.admin import ImportMixin
from import_export import resources, fields
from .models import MyModel
class MyModelResource(resources.ModelResource):
title = fields.Field(attribute='title',
column_name='Title')
field_one = fields.Field(attribute='field_one',
column_name='Field one')
def get_instance(self, instance_loader, row):
# Returning False prevents us from looking in the
# database for rows that already exist
return False
class Meta:
model = MyModel
fields = ('title', 'field_one')
export_order = fields
class MyModelAdmin(ImportMixin, admin.ModelAdmin):
resource_class = MyModelResource
admin.site.register(MyModel, MyModelAdmin)
Related
I am learning Django 2.2, I am trying to display the user name of my model named 'Profile' but instead I have : Profile objet(3), Profile Object(4)
Here is the code in Profile Apps=> models.py :
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import FileExtensionValidator
# Create your models here.
class Profile(models.Model):
name = models.ForeignKey(User, on_delete=models.CASCADE)
website = models.URLField(blank=True)
avatar = models.ImageField(upload_to='uploads/img', validators=[FileExtensionValidator(allowed_extensions=['png'])], blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
#property
def get_created(self):
return self.created.strftime("%m/%d/%Y, %H:%M:%S")
def __str__(self):
return "{}-{}".format(self.name, self.get_created)
Here is the code in Profile Apps=> admin.py :
from django.contrib import admin
from .models import Profile
# Register your models here.
admin.site.register(Profile)
Here is the code in Profile Apps=> init.py :
default_app_config = "profiles.apps.ProfilesConfig"
I have this, instead of user name:
I am a Django beginner and I am trying to make read-only a 'price' field for an order. I think, based on what I have understood, this cannot be done inside the model itself, but rather inside a form.
Since I am using a CreateView generic view, I thought this could have been done by setting the attribute disabled equal to True, as said here.
so what I have done is, in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .models import Order
from django import forms
# Create your views here.
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
meal_price = forms.DecimalField(disabled=True)
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
But this doesn't work.
Here is my models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
Can anybody give me a hint?
Please consider that I am still learning so I would prefer coded answers to descriptive ones.
Thank you in advance
Ok, thanks to dirkgroten, I have worked out the answer.
Basically what is needed (in my case) is:
an Order model in models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
an OrderForm(ModelForm) in forms.py that modifies the price field setting the disabled attribute to true
from django.forms import ModelForm
from .models import Order
from django import forms
class OrderForm(ModelForm):
meal_price = forms.DecimalField(max_digits=5, decimal_places=2, disabled=True)
class Meta:
model = Order
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
an OrderView(CreateView) in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .forms import OrderForm
# Create your views here.
class OrderView(CreateView):
form_class = OrderForm
template_name = 'home.html'
I have no experience with Django's CreateView but from what I read it works similar to a separate form. You could try something like this:
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
def __init__(self, *args, **kwargs):
super(CreateOrderView, self).__init__(*args, **kwargs)
self.fields['meal_price'].widget.attrs['disabled'] = True
From my experience, the disabled attribute will be good for security reasons as far as protecting against the user editing the HTML and changing the value. However you won't be able to access this value when passed into a clean method. If you need to perform actions on the value you should change 'disabled' to 'readonly', but you won't have the same data protection that disabled offers.
I am suspecting that transaction.atomic() does not commit my instance to the database during testing. The problem might comes from multiple databases
from django.db import models
from model_utils.models import TimeStampedModel
class PIIField(TimeStampedModel):
"""
Personal Information Identifier configuration model
This model is M2M on `Plan`
"""
name = models.CharField(max_length=30)
detail = models.CharField(max_length=50, null=True, blank=True)
order = models.SmallIntegerField(unique=True)
def __str__(self):
return self.name
class Plan(timestamp.Model, models.Model):
class Meta:
db_table = "catalog_plans"
product = models.ForeignKey(Product,
on_delete=models.CASCADE,
related_name="plans")
coverages = models.ManyToManyField(Coverage, through="PlanCoverage")
code = models.TextField(null=True)
name = models.TextField()
pii_fields = models.ManyToManyField(PIIField, related_name='plans', related_query_name='plan')
Here is my tests.py
from django.db import transaction
from django.test import TransactionTestCase
from model_mommy import mommy
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APIClient
from catalog.models import Plan
from pii_fields.models import PIIField
class PIIFieldTestCase(TransactionTestCase):
databases = {'default', 'product', 'customer'}
def setUp(self) -> None:
with transaction.atomic():
self.plan = mommy.make(Plan, code='88', name='Non risky life') # single `plan` with no` pii_fields` attached
self.field_1 = mommy.make(PIIField, name='first_name', detail='text box', order=1)
self.field_2 = mommy.make(PIIField, name='last_name', detail='text box', order=2)
self.field_3 = mommy.make(PIIField, name='weight', detail='real number', order=3)
self.field_4 = mommy.make(PIIField, name='nationality', detail='drop down', order=4)
self.plan.pii_fields.set([self.field_1, self.field_2, self.field_3, self.field_4])
def test_get(self):
"""
Get the endpoint and see the payload sample
:return:
"""
client = APIClient()
url = reverse('api:pii_field-detail', args=[self.plan.id])
res = client.get(url)
self.assertEqual(status.HTTP_200_OK, res.status_code)
Error:
django.db.utils.IntegrityError: insert or update on table "catalog_plans_pii_fields" violates foreign key constraint "catalog_plans_pii_fi_piifield_id_58130345_fk_pii_field"
DETAIL: Key (piifield_id)=(1) is not present in table "pii_fields_piifield".
Question:
How to test my database and viewsets?
I have to use another syntax to add my records. I had tried bulk_create, but it does not work
self.plan.pii_fields.create(name='first_name', detail='text box', order=1)
self.plan.pii_fields.create(name='last_name', detail='text box', order=2)
self.plan.pii_fields.create(name='weight', detail='real number', order=3)
self.plan.pii_fields.create(name='nationality', detail='drop down', order=4)
Following are my apps and respective models:
Project name: django03
app: home
home/model.py
from __future__ import unicode_literals
from django.db import models
from django.conf import settings
# Create your models here.
User = settings.AUTH_USER_MODEL
HOME_TYPE = (
('1','1'),
('2','2'),
('3','3'),
)
class Home(models.Model):
home_owner = models.ForeignKey(User,null=False, verbose_name='Owner')
hometype= models.CharField(max_length=100, null=False, default=1,
choices=HOME_TYPE, verbose_name='Home Type')
licenseid= models.CharField(max_length=200, null=False, unique=True,
verbose_name='License ID')
archive = models.BooleanField(default=False)
def __str__(self):
return self.licenseid
app: furniture
furniture/model.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
# Create your models here.
User = settings.AUTH_USER_MODEL
FURNITURE_DATA_IMPORT_SOURCE= (
('0', '0'),
('1', '1'),
('2', '2'),
)
class Furniture(models.Model):
furniture_owner = models.ForeignKey(User, verbose_name='User')
furniture_imported_via = models.CharField(max_length=200, default="0", null=False, choices=FURNITURE_DATA_IMPORT_SOURCE, verbose_name='Source of import')
furniture_title = models.CharField(max_length=100, null=False, verbose_name='Furniture title')
furniture_description = models.TextField(max_length=250, verbose_name='Furniture description')
archive = models.BooleanField(default=False)
def __str__(self):
return self.furniture_title
app:mappings
mappings/model.py
from __future__ import unicode_literals
from django.db import models
from home.models import Home
from furniture.models import Furniture
class HomeFurnitureMapping(models.Model):
home = models.OneToOneField(
Home,
on_delete=models.CASCADE,
null=False,
unique=True,
verbose_name='Home'
)
furniture = models.OneToOneField(
Furniture,
on_delete=models.CASCADE,
null=False,
unique=True,
verbose_name='Furniture'
)
app: furnitureupdates
furnitureupdates/model.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from mappings.models import HomeFurnitureMapping
# Create your models here.
class FurnitureUpdate(models.Model):
mapping_id = models.OneToOneField(
HomeFurnitureMapping,
on_delete=models.CASCADE,
null=False,
unique=True,
verbose_name='Mapping ID'
)
update_status = models.IntegerField(null=False, default=1)
update_date = models.DateField(auto_now_add=True, null=False, verbose_name='Update date')
update_time = models.TimeField(auto_now_add=True, null=False, verbose_name='Update time')
def __str__(self):
return self.mapping_id
My questions are:
How to create/update a record in "FurnitureUpdate" table after I save/update "Furniture" form from admin panel?
How to create/update a record in "FurnitureUpdate" table after I save/update "HomeFurnitureMapping" form from admin panel
And can this functionality to update "FurnitureUpdate" table be retained if I use django-excel like bulk data upload packages?
Update:
I tried django signals, by adding method in "HomeFurnitureMapping" class:
# method for updating
def update_on_home_furniture_mapping(sender, instance, **kwargs):
print ('ENTERED')
print(instance.id)
m_id = instance.id
from updates.models import FurnitureUpdate
FurnitureUpdate.objects.create(mapping_id = m_id)
print ('Furniture Update created!')
# register the signal
post_save.connect(update_on_tag_product_mapping, sender= HomeFurnitureMapping)
But I get the following error on form submission in admin panel.
Error: "FurnitureUpdate.mapping_id" must be a "HomeFurnitureMapping" instance.
your last error fix by remove id:
replace
FurnitureUpdate.objects.create(mapping_id = m_id)
to
FurnitureUpdate.objects.create(mapping_id = instance)
by default in the db django added _id to the name of columns, and in your case columns inside database looks like COLUMN_NAME_id_id double id at the end, so if you want send foreign key as integer you need use double _id_id, but for single _id you need send an instance.
I use django-primate. I override standard user class with 3 new fields:
from primate.models import UserBase, UserMeta
from django.db import models
class CustomUser(UserBase):
__metaclass__ = UserMeta
name = models.CharField(max_length=500, default='Jon Deg')
jabber_id = models.CharField(max_length=150, default='jabber#jabber.org')
title = models.CharField(max_length=20, blank=True)
And, of course, add admin.py:
from primate.admin import UserAdminBase
from django.contrib import admin
from django.contrib.auth.models import User
class UserAdmin(UserAdminBase):
pass
admin.site.register(User, UserAdmin)
Now in django-admin I can manage only 'name' and some other standard auth fields. How can I add, for example, jabber_id to django-admin?