I have the following model:
class A(models.Model):
name = models.CharField(max_length=50)
content_type = models.ForeignKey(ContentType)
This model is supposed to be the root model in some inheritance tree and content_type attribute is a kind of a hint about what type is really stored.
Obviously, I should calculate content_type transparently upon A instance creation. I think, in __init__. But there is a problem - there are two main contexts in which A instances are created:
a = A(name='asdfdf') # here we must fill in content_type
by QuerySet machinery with *args tuple. In this case I shouldn't fill in content_type
So, I'm trying to write:
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
if self.content_type is None: # << here is the problem
self.content_type = ContentType.objects.get_for_model(self)
The thing is self.content_type is ReverseSingleRelatedObjectDescriptor instance with __get__ overriden so that it throws in case value is not set. Yes, I can do following:
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
try:
self.content_type
except Exception, v:
self.content_type = ContentType.objects.get_for_model(self)
But I don't like it. Is there a more 'polite' way to check if the ForeignKey attribute is set?
Does it work if you examine self.content_type_id instead of self.content_type?
Related
I need to check if other models already created, have a field filled .
If another model has the field with any value, the current model that attempts to create should not happen. And if possible send an error message.
This is my current code:
class Video(models.Model):
#####
# Fields of model
#####
def save(self, force_insert=False, force_update=False, *args, **kwargs):
some_video = Video.objects.all().filter(field_boolean=True).first()
if not some_video:
# Save current model
super(Video, self).save(force_insert, force_update, *args, **kwargs)
else:
# avoid save method for the current model created and send error message
What am I doing wrong or what I'm missing? What is the correct way to do this?
Firstly, you do not need to use all() and filter() together. Secondly, use exists() instead of first(). It returns True if the QuerySet contains any results, and False if not. This tries to perform the query in the simplest and fastest way possible.
class Video(models.Model):
name = models.CharField(max_length=30)
field_boolean = models.BooleanField()
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if Video.objects.filter(field_boolean=True).exists():
print('Video with field_boolean=True exists')
else:
super(Video, self).save(*args, **kwargs)
I have an attribute on a model that I don't want another developer to be able to go into the Django shell and change. Anyone know how to do this? I tried overwriting the save method on that model but I can't determine if that attribute has been changed.
Well I figured out how to accomplish this. Another developer could always change the code but this raises an error saying that's not what they're supposed to do.
class myModel(models.Model):
uuid = UUIDField('UUID', primary_key=True, default=uuid4)
model_type = models.ForeignKey(ModelType)
# override the Press model __init__ method to store initial press_type
def __init__(self, *args, **kwargs):
super(myModel, self).__init__(*args, **kwargs)
self.__model_type = self.model_type
# override the save method to prevent updates to press_type
def save(self, *args, **kwargs):
# raise an exception if press_type was changed since initialized
if self.pk and self.__model_type != self.model_type:
raise Exception('The model_type field cannot be changed once set.')
super(myModel, self).save(*args, **kwargs)
Is there a way to modify the data obtained from the model before inserting it in the form?
Here's my model:
class SomeData(models.Model):
Some_No = models.ForeignKey('SomeDef', on_delete=models.PROTECT)
Some_Val = models.DecimalField(max_digits=10, decimal_places=3, verbose_name=_('Some_Val'))
And here's my form:
#autostrip
class SomeForm(forms.ModelForm):
class Meta:
model = models.SomeData
fields = ('Some_Val', 'Some_No')
def __init__(self, *args, **kwargs):
super(SomeForm, self).__init__(*args, **kwargs)
self.fields['Some_No'].label = _(' ')
def clean_some(self):
some = None
some_id = self.cleaned_data['some']
some = models.SomeDef.objects.get(Some_No=some_id)
return some
def save(self, something, *args, **kwargs):
orig_commit = kwargs.get('commit', True)
kwargs['commit'] = False
ri = super(SomeForm, self).save(*args, **kwargs)
ri.Some_No = something
if orig_commit:
try:
ri.save()
except ValidationError as e:
raise ValidationError
return ri
The data saved inside of the models is a bit different from what I want to show in the forms when these are populated with data. However, I cannot figure out how to do it in a smart way.
Using the pre_save signal. Signals
You can set the initial value in the __init__ method:
def __init__(self, *args, **kwargs):
super(SomeForm, self).__init__(*args, **kwargs)
self.fields['Some_No'].initial = f(self.instance)
You can pass an initial data dictionary argument to your form, when you instantiate it into your view.
I am fairly new to Python + Django and I am stuck with the following problem. I have created a custom ModelField like:
class MyField(models.TextField):
def __init__(self, *args, **kwargs):
super(MyField, self).__init__(*args, **kwargs)
def pre_save(self, model_instance, add):
# custom operations here
# need access to variable xyz
The model using this field looks something like:
class MyModel(models.Model):
my_field = MyField()
def __init__(self, model, xyz, *args, **kwargs):
self.instance = model
# how to pass xyz to ModelField before pre_save gets called?
self.xyz = xyz
def save(self, *args, **kwargs):
if self.instance:
self.my_field = self.instance
Q: Like it says in the comment, is there a way to pass a variable to the ModelField instance at runtime, ideally before my_field.pre_save() gets called?
You don't need to do anything to pass the xyz variable on -- it is an instance variable on the model, so it is already present in the model_instance variable that gets passed to pre_save()
class MyField(models.TextField):
def pre_save(self, model_instance, add):
...
# Access model_instance.xyz here
...
# Call the superclass in case it has work to do
return super(MyField, self).pre_save(model_instance, add)
I have a custom form to which I would like to pass a parameter.
Following this example I came up with the following code :
class EpisodeCreateForm(forms.Form):
def __init__(self, *args, **kwargs):
my_arg = kwargs.pop('my_arg')
super(EpisodeCreateForm, self).__init__(*args, **kwargs)
my_field = forms.CharField(initial=my_arg)
But I get the following error:
Exception Value: name 'my_arg' is not defined
How can I get it to recognize the argument in the code of the form ?
You need to set the initial value by referring to the form field instance in __init__. To get access to the form field instance in __init__, put this before the call to super:
self.fields['my_field'].initial=my_arg
And remove initial=my_arg from where you declare my_field because at that point (when class is declared) my_arg is not in scope.
The thing is that my_field is initialized when the class is created, but my_arg is initialized when a new instance is created, far too late for my_field to know its value. What you can do is initialize my_field in __init__ too:
def __init__(self, *args, **kwargs):
my_arg = kwargs.pop('my_arg')
super(EpisodeCreateForm, self).__init__(*args, **kwargs)
if not self.my_field:
self.my_field = my_arg
This code is executed once at import time:
my_field = forms.CharField(initial=my_arg)
and this code is executed on form instance creation:
my_arg = kwargs.pop('my_arg')
super(EpisodeCreateForm, self).__init__(*args, **kwargs)
So this won't work this way. You should set initial value for the field in your __init__ method.
By the way, all this seems unnecessary, why don't use 'initial' keyword in a view?
Considering your comment, I would do this:
class EpisodeCreateForm(forms.Form):
def __init__(self, *args, **kwargs):
self.my_arg = kwargs.pop('my_arg')
kwargs.setdefault('initial', {})['my_field'] = self.my_arg
super(EpisodeCreateForm, self).__init__(*args, **kwargs)
def save(self):
do_something(self.my_arg)
...
super(EpisodeCreateForm, self).save()
my_field = forms.CharField()
Passing initial to the superclass and letting it do the work seems cleaner to me than directly setting it on the field instance.
You simply need to pop your arg before super() and put it in the fields dictionnary after super() :
class EpisodeCreateForm(forms.Form):
my_field = forms.CharField(label='My field:')
def __init__(self, *args, **kwargs):
my_arg = kwargs.pop('my_arg')
super(EpisodeCreateForm, self).__init__(*args, **kwargs)
self.fields['my_arg'].initial = my_arg
Then, simply call
form = EpisodeCreateForm (my_arg=foo)
As an example, say you have a table of Episodes, and you want to show the availables ones in a choices menu, and select the current episode. For that, use a ModelChoiceField:
class EpisodeCreateForm(forms.Form):
available_episode_list = Episode.objects.filter(available=True)
my_field = forms.ModelChoiceField(label='My field:',
queryset=available_episode_list)
def __init__(self, *args, **kwargs):
cur_ep = kwargs.pop('current_episode')
super(EpisodeCreateForm, self).__init__(*args, **kwargs)
self.fields['current_episode'].initial = cur_ep