How can a ChoiceField.choices callable know what choices to return? - django-forms

In Django 1.8, the ChoiceField's choices argument can accept a callable:
def get_choices():
return [(1, "one"), (2, "two")]
class MyForm(forms.Form):
my_choice_field = forms.ChoiceField(choices=get_choices)
In the above example, get_choices() always returns the same choices. However, being able to assign a callable to choices does not make much sense unless that callable knows something like, say, an object id, each time it is called. How can I pass such a thing to it?

You can't do it in the form declaration because the CallableChoiceIterator calls the function without arguments that he gets from here.
Doing in the __init__ Form method is easier than creating your own ChoiceField I guess. Here is what I suggest:
class MyForm(forms.Form):
my_choice_field = forms.ChoiceField(choices=())
def __init__(self, *args, **kwargs):
# Let's pass the object id as a form kwarg
self.object_id = kwargs.pop('object_id')
# django metaclass magic to construct fields
super().__init__(*args, **kwargs)
# Now you can get your choices based on that object id
self.fields['my_choice_field'].choices = your_get_choices_function(self.object_id)
That supposes that you have some Class Based View that looks that has a method like this :
class MyFormView(FormView):
# ...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['object_id'] = 'YOUR_OBJECT_ID_HERE'
return kwargs
# ...
P.S : The super() function call supposes you are using python 3

The reason it's possible to set a callable like that is to avoid situations where you're using models before they're ready.
forms.py
class Foo(ModelForm):
choice_field = ChoiceField(choices=[
user.username.lower() for user in User.objects.all()
])
Were forms.py imported before models were ready, (which it probably is because views.py generally likes to import it, and urls.py generally likes to import that, and urls.py is imported by the startup machinery), it will raise an exception due to trying to do ORM stuff before all the apps are imported.
The correct way is to use a callable like so:
def lower_case_usernames():
return [user.username.lower() for user in User.objects.all()]
class Foo(ModelForm):
choice_field = ChoiceField(choices=lower_case_usernames)
This also has the benefit of being able to change without restarting the server.

Related

Decent way to add a validator for DRF Field class child

I have a custom DRF field element that looks like this:
class TimestampField(serializers.DateTimeField):
def __init__(self, allow_future=True, *args, **kwargs):
self.allow_future = allow_future
def some_sort_of_validator(...): # Don't know how to do that
if not self.allow_future:
if value > timezone.now():
raise ValidationError('...')
Basically, I want to do some custom validation for that field elements. For example, I want to assure that future dates are prohibited. Looks like I need to add something that is refered to as validator in the docs. And I wonder how to do that correctly, so that not to kill native validators. I found nothing regarding this neither in the DRF doc, nor in SO.
There is an article in the docs about writing validators and a section about writing custom validators.
in your case, something like this should work
class TimestampValidator:
def __init__(self, allow_future):
self.allow_future = allow_future
def __call__(self, value):
if not self.allow_future:
if value > timestamp.now():
raise ValidationError('...')
and to use it in your actual serializer
class MySerializer((serializers.Serializer):
timestamp = serializers.DateTimeField(validators=[TimestampValidator(allow_future=True)])
# .. the rest of your serializer goes here
you can also check the code for the built-in validators to see how they are done

Django Forms - is it advisable to change user submitted data in the clean method

I have the following code in my Django project:
def my_view(request):
form = MyForm(request.POST):
if form.is_valid():
instance = form.save(commit = False)
instance.some_field = 'foo'
form.save()
#...
The question is, is it advisable to rewrite this the following way:
forms.py
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# ...
def clean(self):
form.some_field = 'foo'
I wonder, if clean method should be used exclusively for data validation, or I could also perform here some business logic stuff, thus making my views more concise and devoid of business logic details.
One of possible advantages of making the assignment in the clean method is that sometimes data validation could be dependent on some_field.
Have you considered putting it in .save?

How do I make a class conditionally return one of two other classes?

I have a design problem.
I'm writing a REST client in ruby. For reasons beyond my control, it has to extend another gem that uses my networks zookeeper instance to do service lookup. My client takes a user provided tier, and based on that value, queries the zookeeper registry for the appropriate service url.
The problem is that I also need to be able to run my client against a locally running version of the service under test. When the service is running locally, zookeeper is obviously not involved, so I simply need to be able to make GET requests against the localhost resource url.
When a user instantiates my gem, they call something like:
client = MyRestClient.new(tier: :dev)
or in local mode
client = MyRestClient.new(tier: :local)
I would like to avoid conditionally hacking the constructor in MyRestClient (and all of the GET methods in MyRestClient) to alter requests based on :local vs. :requests_via_the_zk_gem.
I'm looking for an elegant and clean way to handle this situation in Ruby.
One thought was to create two client classes, one for :local and the other for :not_local. But then I don't know how to provide a single gem interface that will return the correct client object.
If MyClient has a constructor that looks something like this:
class MyClient
attr_reader :the_klass
def initialize(opts={})
if opts[:tier] == :local
#the_klass = LocalClass.new
else
#the_klass = ZkClass.new
end
#the_klass
end
end
then I end up with something like:
test = MyClient.new(tier: :local)
=> #<MyClient:0x007fe4d881ed58 #the_klass=#<LocalClass:0x007fe4d883afd0>>
test.class
=> MyClient
test.the_klass.class
=> LocalClass
those who then use my gem would have to make calls like:
#client = MyClient.new(tier: :local)
#client.the_klass.get
which doesn't seem right
I could use a module to return the appropriate class, but then I'm faced with the question of how to provide a single public interface for my gem. I can't instantiate a module with .new.
My sense is that this is a common OO problem and I just haven't run into it yet. It's also possible the answer is staring me in the face and I just haven't found it yet.
Most grateful for any help.
A common pattern is to pass the service into the client, something like:
class MyClient
attr_reader :service
def initialize(service)
#service = service
end
def some_method
service.some_method
end
end
And create it with:
client = MyRestClient.new(LocalClass.new)
# or
client = MyRestClient.new(ZkClass.new)
You could move these two into class methods:
class MyClient
self.local
new(LocalClass.new)
end
self.dev
new(ZkClass.new)
end
end
And instead call:
client = MyRestClient.local
# or
client = MyRestClient.dev
You can use method_missing to delegate from your client to the actual class.
def method_missing(m, *args, &block)
#the_class.send(m, *args, &block)
end
So whenever a method gets called on your class that doesn't exist (like get in your example) it wil be called on #the_class instead.
It's good style to also define the corresponding respond_to_missing? btw:
def respond_to_missing?(m, include_private = false)
#the_class.respond_to?(m)
end
The use case you are describing looks like a classic factory method use case.
The common solution for this is the create a method (not new) which returns the relevant class instance:
class MyClient
def self.create_client(opts={})
if opts[:tier] == :local
LocalClass.new
else
ZkClass.new
end
end
end
And now your usage is:
test = MyClient.create(tier: :local)
=> #<LocalClass:0x007fe4d881ed58>
test.class
=> LocalClass

Rails 3, confused about 'before_create :some_method' ... WHEN does some_method do its thing?

we have model helper (used by several different models) called set_guids that sets self.theguid to a random string. Been using it for a long time, we know it works.
in a new model 'Dish' we created, we have
before_create :set_guids (NOTE: no other before/after/validation, just this)
def do_meat_dish
( this is invoked by #somemeat.do_meat_dish in the Dish contoller )
( it manipulated the #somemeat object using self.this and self.that, works fine)
( THEN sometimes it creates a new object of SAME MODEL type )
( which is handled differently)
#veggie = Dish.new
#veggie.do_veggie_dish
end
def do_veggie_dish
recipe_str = "add the XXXX to water"
recipe_str.gsub!("XXXX", self.theguid) *** the PROBLEM: self.theguid is nil
end
as soon as we execute veggie = Dish.new shouldn't veggie.theguid be initialized?
Note we have not saved the new object yet... but the before_create should still have done its thing, right?
it is something to do with create a new instance of a model inside a method for the same model?
is it something with using # for the variables?
Additional note: if we comment out the line trying to access self.theguid everything else works fine ... it's ONLY the value (supposedly) set by the before_create set_guids that is nil instead of being a guid.
before_create is called only before the object is saved to the database the first time. That's why you get nil.
I suggest that you use after_initialize callback instead. Be careful though, since after_initialize will be called whenever the document is new or loaded from the db, that way you will have new guids every time you get the document, which is not what you want. So I suggest you do something like:
def set_guids
return unless theguid.nil?
.....
end
As another solution, if you don't want to change the after_create callback above, you can do something like:
def theguid
super || set_guids
end
That should let you go also.

Alternative initialize for a Class to avoid processing already known information

I have a class, Autodrop, that contains several methods , a.o. 'metadata', that call an external API (dropbox). They are slow.
However, I already often have that metadata around when initializing the AutodropImage, so I should make the methods smarter.
What I have in mind is this:
class Autodrop
include Dropbox
attr_reader :path
def initialize(path)
#path = path
end
def self.from_entry(drop_entry)
#drop_entry = drop_entry
self.initialize(#drop_entry.path)
end
def metadata
if #drop_entry = nil
return heavy_lifting_and_network_traffic
else
return #drop_entry.metadata
end
end
#...
end
Now, I would expect to call
entry = BarEntry.new()
foo = Autodrop.from_entry(entry)
foo.metadata
In order to avoid that heavy lifting and network traffic call.
But this does not work. And somehow, in all my newbieness, I am sure I am goind at this all wrong.
Is there a term I should look for and read about first? How would you go for this?
Note, that the examples are simplified: in my code, I inherit AutodropImage < Autodrop for example, which is called from withing AutodropGallery < Autodrop. The latter already knows all metadata for the AutodropImage, so I mostly want to avoid AutodropImage going over the heavy lifting again.
You are creating an instance variable #drop_entry in your class method from_entry and obviously it wont be available to your object that you are creating in this method. One workaround is to pass it as a parameter when you are initializing the class. It should work if you do the following modifications:
In your from_entry class method change
self.initialize(#drop_entry)
to
new(#drop_entry)
Modify initialize method to:
def initialize(drop_entry)
#drop_entry = drop_entry
#path = #drop_entry.path
end
Or if your class is tied up to pass only the path parameter, ie. you dont want to change the other existing code then you can use an optional parameter drop entry like so
def initialize(path, drop_entry=nil)
You would need to cache the metadata in a class variable.
Edit: Or in a class level instance variable.
Maybe this read will help: http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/

Resources