I have a model which is refered to as a foreignkey with on_delete set to SET_DEFAULT. Because of that, I need to provide this model with a default item. I created a static method which does just that.
class ScheduleChoice(models.Model):
"""
This model allows users to define crontab schedules.
"""
label = models.CharField(max_length=256, verbose_name="Label", unique=True)
job_schedule = models.CharField(
max_length=256,
default="0 0 * * *", verbose_name="Crontab"
)
#staticmethod
def get_default_schedule_choice():
"""
Because some models rely on ScheduleChoice and need a default value,
we need to give them a default ScheduleChoice.
"""
try:
choice = ScheduleChoice.objects.get_or_create(
label="Every minute",
job_schedule="* * * * *"
)
except ProgrammingError:
choice = None
return choice
#classmethod
def get_choice_count(cls):
"""
Returns how many schedule choices have been defined.
"""
return len(cls.objects.all())
class Meta:
verbose_name = "schedule choice"
verbose_name_plural = "schedule choices"
def __str__(self):
return self.label
class MyOtherModel(models.Model):
"""
Model using ScheduleChoices.
"""
job_schedule = models.ForeignKey(
"ScheduleChoice",
on_delete=models.SET_DEFAULT,
default=ScheduleChoice.get_default_schedule_choice(),
verbose_name="Schedule"
)
activated = models.BooleanField(default=False, verbose_name="activated")
I am able to run makemigrations and migrate without issue.
My problem starts when I modifiy my models and try to use makemigrations again in order to update the migrations files. I get the error :
ValueError: Cannot serialize: <ScheduleChoice: Every minute>
There are some values Django cannot serialize into migration files.
For more, see https://docs.djangoproject.com/en/2.2/topics/migrations/#migration-serializing
I tried to apply this answer, but it didn't help. Why does Django need to serialize my default value ? Why does it only do so after the first migration has successfully ended ?
I can always use reset_db and do my migration, but it is not acceptable in my production environment.
How can I fix this ?
Django needs to serialize your models to make migration files for them. Hence it also needs to serialize most of the attributes you set on the model fields (default included). Currently you define a method and directly call that instead of providing the method as the default, also your method returns a tuple with ScheduleChoice and a boolean.
Django can serialize booleans but not the model instance (for the migration) hence you get an error, not to mention the tuple would have caused an error anyway. You should not call the method and instead just pass the method as the default, also instead of returning the instance return only the pk, also ideally this method should simply be a function:
class ScheduleChoice(models.Model):
"""
This model allows users to define crontab schedules.
"""
label = models.CharField(max_length=256, verbose_name="Label", unique=True)
job_schedule = models.CharField(
max_length=256,
default="0 0 * * *", verbose_name="Crontab"
)
#classmethod
def get_choice_count(cls):
"""
Returns how many schedule choices have been defined.
"""
return len(cls.objects.all())
class Meta:
verbose_name = "schedule choice"
verbose_name_plural = "schedule choices"
def __str__(self):
return self.label
def get_default_schedule_choice():
choice, _created = ScheduleChoice.objects.get_or_create(
label="Every minute",
job_schedule="* * * * *"
)
# Why would programming error occur?
return choice.pk # Return the pk
class MyOtherModel(models.Model):
"""
Model using ScheduleChoices.
"""
job_schedule = models.ForeignKey(
"ScheduleChoice",
on_delete=models.SET_DEFAULT,
default=get_default_schedule_choice, # Don't call the function, only pass it
verbose_name="Schedule"
)
activated = models.BooleanField(default=False, verbose_name="activated")
Related
My issue:
I'm trying to add some models to my database through the admin path on my django project, but this error keeps popping up when I try to create an instance of my model. The clean function exists to ensure that each Pokemon model can only have up to 2 types selected from a list of all possible types.
My code:
from django.db import models
from django.core.validators import MinValueValidator as min, MaxValueValidator as max
from django.core.exceptions import ValidationError
class PokeType(models.Model):
poke_type = models.CharField(max_length=15)
def __str__(self):
return self.poke_type
#Current generation of games for gen_added field
gen = 8
class Pokemon(models.Model):
poke_name = models.CharField(max_length=30)
poke_type = models.ManyToManyField(PokeType, blank=True, null=True)
evolves_from = False
evolves_into = False
gen_added = models.PositiveIntegerField(validators=[min(1), max(gen)])
def clean(self):
#Allow max of 2 poke_types to be selected
if self.poke_type.count() > 2:
raise ValidationError('A Pokemon has a maximum of two types.')
class Meta:
verbose_name_plural = 'Pokemon'
abstract = True
class CustomPokemon(Pokemon):
name = models.CharField(max_length=30)
level = models.PositiveIntegerField(blank=True, null=True)
def __str__(self):
return self.name
What I (kind of) know:
Based on what I've seen from other people asking this question, I need to save the instance of my model before I can use the many-to-many relationship in the clean function. Also, I do not get the error when my clean function is commented out, so I've narrowed the issue down to that portion of the code.
What I've tried:
I thought that changing my if statement to check for the presence of the poke_type variable would help - i.e.
def clean(self):
#Allow max of 2 poke_types to be selected
if self.poke_type and self.poke_type.count() > 2:
raise ValidationError('A Pokemon has a maximum of two types.')
but the error is still raised. As I noted above, removing that function also allows me to proceed without raising that specific error, but breaks my ability to limit the max number of types selectable.
Next, I tried creating a save function to save the model and then give the model the poke_type attribute after that:
def save(self):
self.save()
self.poke_type = models.ManyToManyField(PokeType, blank=True, null=True)
Is there a way to save the instance of the model as it's being added through the admin site or is that not possible?
Make use of the m2m_changed signal instead
def poke_type_changed(sender, **kwargs):
if kwargs['instance'].poke_type.count() > 2:
raise ValidationError('A Pokemon has a maximum of two types.')
m2m_changed.connect(poke_type_changed, sender=Pokemon.poke_type.through)
I'm trying to figure out how to properly create a model which has to have two attributes (Models) and it should meet some conditions.
So the model is called Job.
The Job represents buying a translation from one language (model Language) to another language (model Language too).
Each Job have to have exactly 1 language_from attribute and exactly 1 language_to attribute.
My old models:
class Language(models.Model):
shortcut = models.CharField(max_length=40)
name = models.CharField(max_length=40)
def __str__(self):
return self.name
class Job(models.Model):
customer = models.ForeignKey(User, related_name='orders')
translator = models.ForeignKey(User, related_name='jobs',null=True)
price = models.FloatField(null=True,blank=True)
language_from = # Didn't know what to put here
language_to = # Didn't know what to put here
def __str__(self):
return '{}: {}'.format(self.customer,)
I was searching for a way how to make it work, I though about some ManyToOne field but I've realised that there is no such field. So I've been adviced to rebuilt my models this way:
There will be no language_from and language_to attributes in Job.
There will be to and from attributes in model Language which
will be ForeignKeys.
So the Job would not have language_from/to attributes, instead of that, there would be attributes in Language model:
class Language(models.Model):
shortcut = models.CharField(max_length=40)
name = models.CharField(max_length=40)
job_from = models.ForeignKey('Job',related_name='language_form',null=True)
job_to = models.ForeignKey('Job',related_name='language_to',null=True)
def __str__(self):
return self.name
This way probably would work correctly but there are many problems.
I can't tell the Django that every Job has to have exactly on
language_form and language_to
There are problems when trying to create a JobCreationForm because I can't add language_from/to fields inside a class Meta:
I think that it is not intuitive and there will be more problems which I don't know yet about
.
class JobCreationForm(forms.ModelForm):
description = forms.CharField(widget=forms.Textarea(attrs={'placeholder': 'Specification'}))
file = forms.FileField()
class Meta:
model = Job
fields = (
'language_from','language_to', 'description', 'file', 'specialist'
)
Exception Value:
Unknown field(s) (language_from, language_to) specified for Job
Do anybody knows what should I do?
First of all, you can't use related name in your fields().
So change
fields = (
'language_from','language_to', 'description', 'file', 'specialist'
)
into
fields = (
'job_from','job_to', 'description', 'file', 'specialist'
)
Next, if you want to have for each job a language from & a language to, these are mandatory fields, so you can do this:
job_from = models.ForeignKey('Job',related_name='language_form')
job_to = models.ForeignKey('Job',related_name='language_to')
(https://docs.djangoproject.com/en/1.9/ref/forms/fields/#required)
I have made my own SearchForm following instructions from Django website, but for some reason if I add my own search field, it doesn't return any results, even when it should return results.
My search_indexes.py:
from haystack import indexes
from my.app.models import MyModel
class MyIndexIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.NgramField(document=True, use_template=True)
my_fieldname = indexes.CharField(model_attr='my_fieldname')
def get_model(self):
return MyModel
My model:
class MyModel(models.Model):
some_field1 = models.CharField(_('Some field 1'), max_length=255)
some_field2 = models.CharField(_('Some field 2'), max_length=255)
my_fieldname = models.CharField(_('My field name'), max_length=255)
My search form:
class MySearchForm(SearchForm):
q = forms.CharField(label="Search", max_length=255, required=False)
my_fieldname = forms.CharField(label="MySearchLabel", max_length=255, required=False)
def search(self):
sqs = super(MySearchForm, self).search()
if self.is_valid() and self.cleaned_data['my_fieldname']:
sqs = sqs.filter(my_fieldname=AutoQuery(self.cleaned_data['my_fieldname']))
return sqs
My urls.py:
urlpatterns += patterns('haystack.views',
url(r'^search/', SearchView(
form_class=MySearchForm
), name='haystack_search'),
)
I have run manage.py rebuild_index, but it doesn't affect. My question is what I am doing wrong here, why I am not getting any results. If I try some queries with q parameters, results are returned normally.
super(MySearchForm, self).search() performs an auto_query() with a default field name of content... As you haven't defined a field like this it will not return any results... Therefore I wouldn't do the super call, but replace with an implementation similar to the original one - but pass a fieldname to auto_query: auto_query(querystring, fieldname='text').
Also make sure to have the template defined correctly if you are searching in it. (If you are chaining multiple filter() calls they will be combined usind and).
Also depending on the search engine you use it will probably offer you a frontend where you can check independently from haystack if the data is indexed properly...
if I write something like
class Chip(models.Model):
name = models.CharField(max_length=16)
shortname = models.CharField(primary_key=True, unique=True, max_length = 16)
def __unicode__(self):
return self.shortname
class ChipStepping(models.Model):
stepping = models.CharField (max_length=16)
ChipShortname = models.ForeignKey('Chip', db_column="ChipShortname")
def __unicode__(self):
return "%s:%s" % (self.ChipShortname, self.stepping)
class ComponentType(models.Model):
name = models.CharField (max_length=32)
ChipStepping = models.ForeignKey('ChipStepping', db_column="ChipStepping")
def __unicode__(self):
return "%s(%s)" % (self.name, self.ChipStepping);
class ComponentVendor(models.Model):
name = models.CharField (unique=True, max_length=16)
products = models.ManyToManyField('ComponentType', through='ComponentVendorProduct', related_name='vendors')
def __unicode__(self):
return "%s" % (self.name)
class ComponentVendorProduct(models.Model):
ComponentVendor = models.ForeignKey('ComponentVendor', db_column="ComponentVendor")
ComponentType = models.ForeignKey('ComponentType' , db_column="ComponentType")
And try to create an admin page for ComponentVendor
class ProductInline(admin.TabularInline):
model = ComponentVendor.products.through
extra = 0
class ComponentVendorAdmin(admin.ModelAdmin):
inlines = [ProductInline]
list_filter = ['products__name']
exclude = ['products']
admin.site.register(ComponentVendor, ComponentVendorAdmin)
The resulting page can take upwards of 30 sec. to load
From some debugging I've done, I found that it repeatedly makes redundant singular queries for ChipStepping and then Chip, with the same argument in the where clause instead of intelligently building a query that can lookup all the data.
This problem is reduced if I remove the foreign key references from the unicode functions of ChipStepping and ComponentType
If there are enough entries in ComponentVendorProducts for a vendor I click on in the admin page, the page can take several minutes!
Is there a way I can reduce the number of database hits on the admin page?
Your problem comes from the fact that Django is doing a DB call everytime you call __unicode__ on a ComponentType instance.
You have two solutions to your issue:
You override your ProductInline's queryset method to include select_related('ChipStepping') (Django 1.3 and superior).
Alternatively, if you want to fix the issue elsewhere too, you might want to change your ComponentType's default manager (objects) get_query_set method to have it include the select_related call.
you might also want to checkout the suggestion given here:
http://blog.ionelmc.ro/2012/01/19/tweaks-for-making-django-admin-faster/
It seems that the choices are evaluated for every row, so using formfield_for_dbfield you can cache the choices as suggested in the link. This saves going to the db for rendering every single dropdown/select box for foreign_keys
I'm having trouble doing an aggregation query on a many-to-many related field.
Here are my models:
class SortedTagManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
orig_query_set = super(SortedTagManager, self).get_query_set()
# FIXME `used` is wrongly counted
return orig_query_set.distinct().annotate(
used=models.Count('users')).order_by('-used')
class Tag(models.Model):
content = models.CharField(max_length=32, unique=True)
creator = models.ForeignKey(User, related_name='tags_i_created')
users = models.ManyToManyField(User, through='TaggedNote',
related_name='tags_i_used')
objects_sorted_by_used = SortedTagManager()
class TaggedNote(models.Model):
"""Association table of both (Tag , Note) and (Tag, User)"""
note = models.ForeignKey(Note) # Note is what's tagged in my app
tag = models.ForeignKey(Tag)
tagged_by = models.ForeignKey(User)
class Meta:
unique_together = (('note', 'tag'),)
However, the value of the aggregated field used is only correct when the model is queried directly:
for t in Tag.objects.all(): print t.used # this works correctly
for t in user.tags_i_used.all(): print t.used #prints n^2 when it should give n
Would you please tell me what's wrong with it? Thanks in advance.
I have figured out what's wrong and how to fix it now :)
As stated in the Django doc:
Django interprets the first Manager defined in a class as the "default" Manager, and several parts of Django will use that Manager exclusively for that model.
In my case, I should make sure that SortedTagManager is the first Manager defined.
2.I should have count notes instead of users:
Count('notes', distinct=True)