How can i create a Array Custom Model Field - django

class PermsField(models.Field):
def db_type(self, connection):
return 'CharField'
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 64
kwargs['verbose_name'] =_('permission')
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
del kwargs["max_length"]
del kwargs["verbose_name"]
return name, path, args, kwargs
I want to create a custom model Field who behaves like an array but i can't use an array.
I don't understand because my code doesn't work, the permsField work like an str. But i want it work like an array (or list) of str

Related

Example of custom model field extending ForeignKey

Can someone give me an example of extending a ForeignKey model field? I tried like this:
class ForeignKeyField(forms.ModelChoiceField):
def __init__(self, *args, **kwargs):
super(ForeignKeyField, self).__init__(Chain.objects.all(), *args, **kwargs)
def clean(self, value):
return Chain.objects.get(pk=value)
class CustomForeignKey(models.ForeignKey):
description = "key from ndb"
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
super(CustomForeignKey, self).__init__(*args, **kwargs)
def db_type(self, connection):
return "ndb"
def to_python(self, value):
# import pdb; pdb.set_trace()
from google.appengine.api.datastore_types import Key
if isinstance(value, Key) is True:
return value.id()
if value is None:
return
return value
def get_db_prep_save(self, value, connection, prepared=False):
save_value = ndb.Key(API_Chain, value.id).to_old_key()
return save_value
def formfield(self, **kwargs):
return models.Field.formfield(self,ForeignKeyField, **kwargs)
I don't know why but if i use __metaclass__ = models.SubfieldBase the to_python gets called with None values and it says that foreign key cannot be null. If I inherit from models.Field it works but not as a foreign Key.
I would like to see how one can extend the functionality of models.ForeignKey. Thanks.
Do you need SubfieldBase? It does some magic behind the scenes so that the field has a descriptor that calls to_python. ForeignKey has different kind of descriptor. I guess ForeignKey descriptor is overridden by the subfieldbase descriptor. In other words, they arenot compatible.

django object is not iterable with custom instance

I am trying to leave my object itself out of the queryset of possible options. Problem is i get the error: 'Country' object is not iterable
Not sure where i am going wrong.
My view:
def edit_country(request, country_id):
country = get_object_or_404(Country, pk=country_id)
country_form = CountryForm(instance=country)
return render(request, 'create_country.html', {'country_form': country_form})
My form init:
def __init__(self, *args, **kwargs):
super(CountryForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['likes'].queryset = Country.objects.exclude(kwargs['instance'])
self.fields['hates'].queryset = Country.objects.exclude(kwargs['instance'])
Where do i go wrong?
Change the order of the method, so you pop the kwarg first. You are sending the kwarg to super.
def __init__(self, *args, **kwargs):
instance = kwargs.pop('instance', None)
#all other stuff

How do I use widgets to add an attribute to a form in Django?

I'm trying to get a placeholder to show up for a form field but I'm doing something wrong with my form fields. Any thoughts? I've got the following classes:
class EditField(forms.CharField):
def __init__(self, *args, **kwargs):
super(EditField, self).__init__(*args, **kwargs)
self.widget = forms.Textarea(attrs={'id':'editor'})
self.label = _('content')
class EditField_typeA(EditField):
def __init__(self, *args, **kwargs):
super(EditField_typeA, self).__init__(*args, **kwargs)
def clean(self, value):
if not (len(re.sub('[ ]{2,}', ' ', value)) < settings.FORM_MIN):
raise forms.ValidationError(_('content must be %s') % settings.FORM_MIN)
return value
class FinalForm(forms.Form):
foo = FooField()
bar = BarField()
text = EditField_typeA()
def __init__(self, data=None, user=None, *args, **kwargs):
super(FinalForm, self).__init__(data, *args, **kwargs)
## THIS IS THE PART THAT ISN'T WORKING
self.fields['text'].widget.attrs['placeholder'] = 'Fill this in'
if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
spam_fields = call_all_handlers('create_anti_spam_field')
if spam_fields:
spam_fields = dict(spam_fields)
for name, field in spam_fields.items():
self.fields[name] = field
self._anti_spam_fields = spam_fields.keys()
else:
self._anti_spam_fields = []
I'm guessing that I'm using widget wrong, or in the wrong place. Can't find the right part of the widget docs to explain what I'm doing wrong though.

Django: Passing arguments to ModelField at runtime

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)

Pass parameter to Form in Django

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