No PyCon for me... I'm just a django novice so may be in the next
years. First I want to learn more about django (and python). Your
application seems really nice. We are working on a platform to show
shop products imported from CSV files into our db and a registration
platform for personal (also import CSV).
Well here is some info. I have the idea that I do something wrong in
the batchimport/forms.py or in the batchimport/batchimport_settings.py
(below the .py info I point out the changes I have made. The project
lives in django_projects/sample_project. As far as I can see I have
made no changes to the util.py. I hope this info makes it more clear.
settings.py
------------------------
# Django settings for batchimport project.
import os, sys
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
DJANGO_PROJECTS_DIR, PROJECT_NAME = os.path.split(PROJECT_DIR)
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', '
your_...@domain.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'postgresql' # 'postgresql_psycopg2',
'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'secret' # Or path to database file if
using sqlite3.
DATABASE_USER = 'secret' # Not used with sqlite3.
DATABASE_PASSWORD = 'secret' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost.
Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not
used with sqlite3.
# Local time zone for this installation. Choices can be found here:
#
http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as
your
# system time zone.
TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here:
#
http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as
not
# to load the internationalization machinery.
USE_I18N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/
media.lawrence.com/"
MEDIA_ROOT = '/home/web/media/sample_project'
# URL that handles the media served from MEDIA_ROOT. Make sure to use
a
# trailing slash if there is a path component (optional in other
cases).
# Examples: "
http://media.lawrence.com", "
http://example.com/media/"
MEDIA_URL = '/media/sample_project/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure
to use a
# trailing slash.
# Examples: "
http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/admin_media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = 'SECRET'
# List of callables that know how to import templates from various
sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'sample_project.urls'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/
django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(DJANGO_PROJECTS_DIR, 'templates'),
"/home/web/django_templates/sample_project"
)
#BATCH_IMPORT_IMPORTABLE_MODELS = (
# 'sample_project.sample_app',
#)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'sample_project.batchimport',
'sample_project.sample_app',
)
batchimport/forms.py
--> I have added sample_project to initial of ImportOptionsForm
------------------------------
from django import forms
from sample_project.batchimport.util import get_model_list,
get_column_choice_list, get_model_fields
from sample_project.batchimport.batchimport_settings import *
import_model_list = get_model_list()
class UploadImportFileForm(forms.Form):
model_for_import = forms.ChoiceField(import_model_list, label='What
are you importing?')
import_file = forms.FileField(label='Select your XLS file:')
class ImportOptionsForm(forms.Form):
show_successful_imports = forms.BooleanField
(initial=sample_project.batchimport.batchimport_settings.BATCH_IMPORT_SHOW_SUCCESSFUL_IMPORTS,
required=False)
show_successful_updates = forms.BooleanField
(initial=sample_project.batchimport.batchimport_settings.BATCH_IMPORT_SHOW_SUCCESSFUL_UPDATES,
required=False)
show_errors = forms.BooleanField
(initial=sample_project.batchimport.batchimport_settings.BATCH_IMPORT_SHOW_ERRORS,
required=False)
stop_on_first_error = forms.BooleanField
(initial=sample_project.batchimport.batchimport_settings.BATCH_IMPORT_STOP_ON_FIRST_ERROR,
required=False)
update_dupes = forms.BooleanField
(initial=sample_project.batchimport.batchimport_settings.BATCH_IMPORT_UPDATE_DUPS,
required=False)
start_row = forms.IntegerField
(initial=sample_project.batchimport.batchimport_settings.BATCH_IMPORT_START_ROW,
required=False)
end_row = forms.IntegerField
(initial=sample_project.batchimport.batchimport_settings.BATCH_IMPORT_END_ROW,
required=False)
def __init__(self, model_for_import, save_file_name, *args,
**kwargs):
super(ImportOptionsForm, self).__init__(*args, **kwargs)
self.process_options = {}
# Initialize several lists to be used later...
self.model_field_names = []
self.relation_info_dict = {}
# Get a list of columns from the uploaded spreadsheet.
# This will be either a list of example values or a list
# of column headers.
xls_column_option_list = get_column_choice_list(save_file_name)
# Get a list of field names from the selected model
# for import.
self.mapping_only = False
model_list = []
if 'relation' in model_for_import:
import_model_info_list = model_for_import.split('%')
model_list.append(import_model_info_list[0])
if len(import_model_info_list) > 1:
model_list.append(import_model_info_list[2])
self.mapping_only = True
else:
model_list = [model_for_import]
for model_name in model_list:
# In this field_tuple_list is the following for each field in the
current model:
# -
field.name: The name of the field.
# - related_model_app_name: Name of the app for any
# related model name (if there is one).
# - related_model_name: Name of the related model
# itself (again, if there is one).
# - related_model_field_name_list: List of fields on the
# related model (for mapping the current model's
# field to the related model).
model_field_tuple_list = get_model_fields(model_name,
self.mapping_only)
# Iterate over this field_tuple_list and create a form field
# for each of the following FOR EACH FIELD in the model:
# - xls_columns_available (list of columns from the spreadsheet)
# - default_value_field (default value if none is in the
spreadsheet)
# - related_field_mapping (list of fields on the related model)
# - id_field (to specify whether this field is in the group of
# fields for this model that together can be used to uniquely
# identify an instance of the model.
for field_tuple in model_field_tuple_list:
base_field_name = field_tuple[0]
full_field_name = model_name + '.' + base_field_name
# XLS column name list for this field.
xls_column_field_name = full_field_name + '-xls_column'
self.model_field_names.append(xls_column_field_name)
initial_value = self._get_initial_value(xls_column_option_list,
base_field_name)
self.fields[xls_column_field_name] = forms.ChoiceField
(xls_column_option_list, label='Spreadsheet column:', required=False,
initial=initial_value)
# ID selection checkmark (see template for example)
is_id_field_name = full_field_name + '-is_id_field'
self.model_field_names.append(is_id_field_name)
if not self.mapping_only:
self.fields[is_id_field_name] = forms.BooleanField
(required=False,initial=True)
else:
self.fields[is_id_field_name] = forms.BooleanField
(required=False,initial=False)
if not self.mapping_only:
related_model_app_name = field_tuple[1]
related_model_name = field_tuple[2]
field_mapping_list = field_tuple[3]
# Get list of fields on the related model as needed.
related_field_choice_list = []
if field_mapping_list:
for related_field_name in field_mapping_list:
related_field_choice_list.append((related_field_name,
related_field_name))
self.relation_info_dict[full_field_name] =
(related_model_app_name,
related_model_name,
related_field_choice_list)
# Default value form field.
default_value_field_name = full_field_name + '-default_value'
self.model_field_names.append(default_value_field_name)
self.fields[default_value_field_name] = forms.CharField
(label="Default value", max_length=100, required=False)
# List of fields on the related model on which to map this field.
mapping_choice_field_name = full_field_name + '-mapping_choice'
self.model_field_names.append(mapping_choice_field_name)
self.fields[mapping_choice_field_name] = forms.ChoiceField
(related_field_choice_list, label='Related model field mapping',
required=False)
self.import_info_dict = self.get_import_info_dict()
def get_import_info_dict(self):
import_info_dict = {}
for field_name in self.model_field_names:
field_name_parts = field_name.split('.')
model_name = field_name_parts[-2]
base_field_name = field_name_parts[-1].split('-')[0]
if not model_name in import_info_dict.keys():
import_info_dict[model_name] = {}
model_field_dict = import_info_dict[model_name]
if not base_field_name in model_field_dict.keys():
model_field_dict[base_field_name] = []
field_list = model_field_dict[base_field_name]
field_list.append(self[field_name])
return import_info_dict
def get_process_options_dict(self):
if not self.process_options:
process_options = {}
process_options['show_successful_imports'] = self
['show_successful_imports']
process_options['show_successful_updates'] = self
['show_successful_updates']
process_options['show_errors'] = self['show_errors']
process_options['stop_on_first_error'] = self
['stop_on_first_error']
process_options['update_dupes'] = self['update_dupes']
process_options['start_row'] = self['start_row']
process_options['end_row'] = self['end_row']
self.process_options = process_options
return self.process_options
def _get_initial_value(self, xls_column_option_list, field_name):
if field_name[-1] == '*':
field_name = ''.join(field_name[:-1])
choice_index = -1
for item in xls_column_option_list:
choice = item[1]
if field_name.lower() == choice.lower():
choice_index = item[0]
break
return str(choice_index)
batchimport/batchimport_settings.py
--> If I add a model in the empty list of
BATCH_IMPORT_IMPORTABLE_MODELS it appears in the html form of /
import_start/ but when I try to upload I get the message: 'NoneType'
object has no attribute '_meta' (AttributeError at /batchimport/
import_options/ -- Exception Location: /home/web/django_projects/
sample_project/batchimport/util.py in get_model_fields, line 155)
-----------------------------------------------
"""
The batchimport_settings.py module initializes itself with defaults
but
allows for the values to be overridden via the django project's
settings
file.
NOTE: These values should be considered CONSTANTS even though I'm kind
of cheating and using them as variables to initialize them here.
"""
import sample_project.settings
def get_setting(setting_name, default):
"""
A simple setting retrieval function to pull settings from the
main settings.py file.
"""
setting = default
print setting_name
print default
try:
setting=getattr(settings, setting_name)
except (AttributeError, NameError):
pass
return setting
# INITIALIZE BATCHIMPORT SETTINGS...
BATCH_IMPORT_START_TEMPLATE = get_setting
('BATCH_IMPORT_START_TEMPLATE', 'batchimport/start.html')
BATCH_IMPORT_OPTIONS_TEMPLATE = get_setting
('BATCH_IMPORT_OPTIONS_TEMPLATE', 'batchimport/options.html')
BATCH_IMPORT_EXECUTE_TEMPLATE = get_setting
('BATCH_IMPORT_EXECUTE_TEMPLATE', 'batchimport/processing.html')
BATCH_IMPORT_RESULTS_TEMPLATE = get_setting
('BATCH_IMPORT_RESULTS_TEMPLATE', 'batchimport/results.html')
# Specify the list of models in your application which are importable
# in batch. If you do not provide a list, the system will use
introspection
# to get a list of ALL models in your application (via
INSTALLED_APPS).
BATCH_IMPORT_IMPORTABLE_MODELS = get_setting
('BATCH_IMPORT_IMPORTABLE_MODELS', [])
# Specify where the uploaded Microsoft Excel file will be saved to the
# system.
# NOTE: This must be a absolute path.
# NOTE: Django must have read/write access to this location.
BATCH_IMPORT_TEMPFILE_LOCATION = get_setting
('BATCH_IMPORT_TEMPFILE_LOCATION', '/tmp/')
# By default, the system does not allow you to import data for fields
# that are not EDITABLE (i.e. in their model field declarations,
you've
# set editable=False). You can override this behavior here:
BATCH_IMPORT_UNEDITABLE_FIELDS = get_setting
('BATCH_IMPORT_UNEDITABLE_FIELDS', False)
# Sometimes you will want to override the value coming in from the XLS
# file with a constant or a dynamically generated value.
# The following setting is a dictionary of values (or callables) per
# each fully specified model field.
# NOTE: You must import the item into your settings file if it is a
# callable.
BATCH_IMPORT_VALUE_OVERRIDES = get_setting
('BATCH_IMPORT_VALUE_OVERRIDES', {})
# The system can show you individual imports, updates,
# or errors individually using the following boolean options.
# Note that True is assumed for all three if no setting is
# present.
BATCH_IMPORT_SHOW_SUCCESSFUL_IMPORTS = get_setting
('BATCH_IMPORT_SHOW_SUCCESSFUL_IMPORTS', True)
BATCH_IMPORT_SHOW_SUCCESSFUL_UPDATES = get_setting
('BATCH_IMPORT_SHOW_SUCCESSFUL_UPDATES', True)
BATCH_IMPORT_SHOW_ERRORS = get_setting('BATCH_IMPORT_SHOW_ERRORS',
True)
# Whether the system should stop on the first error
# or process the entire uploaded spreadsheet and show
# errors afterwards.
BATCH_IMPORT_STOP_ON_FIRST_ERROR = get_setting
('BATCH_IMPORT_STOP_ON_FIRST_ERROR', False)
# Whether or not to update duplicates or simply
# ignore them. Note that duplicates are determined
# based on the user's specification of model fields
# as identification fields. If these are not set, a duplicate
# must match at all column/fields.
BATCH_IMPORT_UPDATE_DUPS = get_setting('BATCH_IMPORT_UPDATE_DUPS',
False)
# If no options are set for start/end row, defaults are used that
# assume (1) the spreadsheet has a header row (indicating that data
# starts on row #2 and (2) the entire spreadsheet is to be processed.
BATCH_IMPORT_START_ROW = get_setting('BATCH_IMPORT_START_ROW', 2)
BATCH_IMPORT_END_ROW = get_setting('BATCH_IMPORT_END_ROW', -1)
On Mar 25, 4:54 pm, Keyton Weissinger <
key...@gmail.com> wrote:
> Hi Michael,
>
> Great to hear from you. I'm sure we can get you squared away. The
> error doesn't sound familiar but we can certainly look at it. Is the
> data or app proprietary? Would it be possible to share with me a
> sample of the data and your models file?
>
> One thought off the top of my head. Are your models in a file/module
> called models(.py)?
>
> You don't happen to be at PyCon do you? We could meet to discuss...
>
> Keyton
>