1 from django.conf import settings
2 from django.template import Template, Context
4 from django.db import models
5 from django.contrib.contenttypes.models import ContentType
9 import djangosphinx.apis.current as sphinxapi
11 __all__ = ('generate_config_for_model', 'generate_config_for_models')
13 def _get_database_engine():
14 if settings.DATABASE_ENGINE == 'mysql':
15 return settings.DATABASE_ENGINE
16 elif settings.DATABASE_ENGINE.startswith('postgresql'):
18 raise ValueError, "Only MySQL and PostgreSQL engines are supported by Sphinx."
20 def _get_template(name):
22 os.path.join(os.path.dirname(__file__), '../apis/api%s/templates/' % (sphinxapi.VER_COMMAND_SEARCH,)),
23 os.path.join(os.path.dirname(__file__), '../templates/'),
27 fp = open(path + name, 'r')
31 t = Template(fp.read())
35 raise ValueError, "Template matching name does not exist: %s." % (name,)
37 def _is_sourcable_field(field):
38 # We can use float fields in 0.98
39 if sphinxapi.VER_COMMAND_SEARCH >= 0x113 and (isinstance(field, models.FloatField) or isinstance(field, models.DecimalField)):
41 if isinstance(field, models.ForeignKey):
43 if isinstance(field, models.IntegerField) and field.choices:
49 # No trailing slashes on paths
50 DEFAULT_SPHINX_PARAMS = {
51 'database_engine': _get_database_engine(),
52 'database_host': settings.DATABASE_HOST,
53 'database_port': settings.DATABASE_PORT,
54 'database_name': settings.DATABASE_NAME,
55 'database_user': settings.DATABASE_USER,
56 'database_password': settings.DATABASE_PASSWORD,
57 'log_file': '/var/log/sphinx/searchd.log',
58 'data_path': '/var/data',
61 # Generate for single models
63 def generate_config_for_model(model_class, index=None, sphinx_params={}):
65 Generates a sample configuration including an index and source for
66 the given model which includes all attributes and date fields.
68 return generate_source_for_model(model_class, index, sphinx_params) + "\n\n" + generate_index_for_model(model_class, index, sphinx_params)
70 def generate_index_for_model(model_class, index=None, sphinx_params={}):
71 """Generates a source configmration for a model."""
72 t = _get_template('index.conf')
75 index = model_class._meta.db_table
77 params = DEFAULT_SPHINX_PARAMS
78 params.update(sphinx_params)
89 def generate_source_for_model(model_class, index=None, sphinx_params={}):
90 """Generates a source configmration for a model."""
91 t = _get_template('source.conf')
93 valid_fields = [f for f in model_class._meta.fields if _is_sourcable_field(f)]
95 # Hackish solution for a bug I've introduced into composite pks branch
96 pk = model_class._meta.get_field(model_class._meta.pk.name)
98 if pk not in valid_fields:
99 valid_fields.insert(0, model_class._meta.pk)
102 index = model_class._meta.db_table
104 params = DEFAULT_SPHINX_PARAMS
105 params.update(sphinx_params)
107 'source_name': index,
110 'primary_key': pk.column,
111 'field_names': [f.column for f in valid_fields],
112 'group_columns': [f.column for f in valid_fields if (f.rel or isinstance(f, models.BooleanField) or isinstance(f, models.IntegerField)) and not f.primary_key],
113 'date_columns': [f.column for f in valid_fields if isinstance(f, models.DateTimeField) or isinstance(f, models.DateField)],
114 'float_columns': [f.column for f in valid_fields if isinstance(f, models.FloatField) or isinstance(f, models.DecimalField)],
121 # Generate for multiple models (search UNIONs)
123 def generate_config_for_models(model_classes, index=None, sphinx_params={}):
125 Generates a sample configuration including an index and source for
126 the given model which includes all attributes and date fields.
128 return generate_source_for_models(model_classes, index, sphinx_params) + "\n\n" + generate_index_for_models(model_classes, index, sphinx_params)
130 def generate_index_for_models(model_classes, index=None, sphinx_params={}):
131 """Generates a source configmration for a model."""
132 t = _get_template('index-multiple.conf')
135 index = '_'.join(m._meta.db_table for m in model_classes)
137 params = DEFAULT_SPHINX_PARAMS
138 params.update(sphinx_params)
141 'source_name': index,
148 def generate_source_for_models(model_classes, index=None, sphinx_params={}):
149 """Generates a source configmration for a model."""
150 t = _get_template('source-multiple.conf')
152 # We need to loop through each model and find only the fields that exist *exactly* the
153 # same across models.
155 return (f.__class__, f.column, getattr(f.rel, 'to', None), f.choices)
157 valid_fields = [_the_tuple(f) for f in model_classes[0]._meta.fields if _is_sourcable_field(f)]
158 for model_class in model_classes[1:]:
159 valid_fields = [_the_tuple(f) for f in model_class._meta.fields if _the_tuple(f) in valid_fields]
162 for model_class in model_classes:
163 tables.append((model_class._meta.db_table, ContentType.objects.get_for_model(model_class)))
166 index = '_'.join(m._meta.db_table for m in model_classes)
168 params = DEFAULT_SPHINX_PARAMS
169 params.update(sphinx_params)
172 'source_name': index,
174 'field_names': [f[1] for f in valid_fields],
175 'group_columns': [f[1] for f in valid_fields if f[2] or isinstance(f[0], models.BooleanField) or isinstance(f[0], models.IntegerField)],
176 'date_columns': [f[1] for f in valid_fields if issubclass(f[0], models.DateTimeField) or issubclass(f[0], models.DateField)],
177 'float_columns': [f[1] for f in valid_fields if isinstance(f[0], models.FloatField) or isinstance(f[0], models.DecimalField)],