I'm building my first form with django, and I'm seeing some behavior that I really did not expect at all. I defined a form class:
class AssignmentFilterForm(forms.Form):
filters = []
filter = forms.ChoiceField()
def __init__(self, *args, **kwargs):
super(forms.Form, self).__init__(*args, **kwargs)
self.filters.append(PatientFilter('All'))
self.filters.append(PatientFilter('Assigned', 'service__isnull', False))
self.filters.append(PatientFilter('Unassigned', 'service__isnull', True))
for i, f in enumerate(self.filters):
self.fields["filter"].choices.append((i, f.name))
When I output this form to a template using:
{{ form.as_p }}
I see the correct choices. However, after refreshing the page, I see the list three times in the select box. Hitting refresh again results in the list showing 10 times in the select box!
Here is my view:
#login_required
def assign_test(request):
pg = PhysicianGroup.objects.get(pk=physician_group)
if request.method == 'POST':
form = AssignmentFilterForm(request.POST)
if form.is_valid():
yes = False
else:
form = AssignmentFilterForm()
patients = pg.allPatients().order_by('bed__room__unit', 'bed__room__order', 'bed__order' )
return render_to_response('hospitalists/assign_test.html', RequestContext(request, {'patients': patients, 'form': form,}))
What am I doing wrong?
Thanks, Pete
This is actually a feature of Python that catches a lot of people.
When you define variables on the class as you have with filters = [] the right half of the expression is evaluated when the class is initially defined. So when your code is first run it will create a new list in memory and return a reference to this list. As a result, each AssignmentFilterForm instance will have its own filters variable, but they will all point to this same list in memory. To solve this just move the initialization of self.filters into your __init__ method.
Most of the time you don't run into this issue because the types you are using aren't stored as a reference. Numbers, booleans, etc are stored as their value. Strings are stored by reference, but strings are immutable meaning a new string must be created in memory every time it is changed and a new reference returned.
Pointers don't present themselves often in scripting language, so it's often confusing at first when they do.
Here's a simple IDLE session example to show what's happening
>>> class Test():
myList = []
def __init__( self ):
self.myList.append( "a" )
>>> Test.myList
[]
>>> test1 = Test()
>>> Test.myList
['a']
>>> test1.myList
['a']
>>> test2 = Test()
>>> test2.myList
['a', 'a']
>>> test1.myList
['a', 'a']
>>> Test.myList
['a', 'a']
I picked up the book Pro Django which answers this question. It's a great book by the way, and I highly recommend it!
The solution is to make BOTH the choice field and my helper var both instance variables:
class AssignmentFilterForm(forms.Form):
def __init__(self, pg, request = None):
super(forms.Form, self).__init__(request)
self.filters = []
self.filters.append(PatientFilter('All'))
self.filters.append(PatientFilter('Assigned', 'service__isnull', False))
self.filters.append(PatientFilter('Unassigned', 'service__isnull', True))
self.addPhysicians(pg)
self.fields['filter'] = forms.ChoiceField()
for i, f in enumerate(self.filters):
self.fields['filter'].choices.append((i, f.name))
Clearing out the choices works but would surely result in threading issues.
You're appending to the PER-CLASS variable self.filters. Make it into a PER-INSTANCE variable instead, by doing self.filters = [] at the start of __init__.
To clarify from some of the other answers:
The fields are, and must be, class variables. They get all sorts of things done to them by the metaclass, and this is the correct way to define them.
However, your filters variable does not need to be a class var. It can quite easily be an instance var - just remove the definition from the class and put it in __init__. Or, perhaps even better, don't make it a property at all - just a local var within __init__. Then, instead of appending to filters.choices, just reassign it.
def __init__(self, *args, **kwargs):
super(forms.Form, self).__init__(*args, **kwargs)
filters = []
filters.append(PatientFilter('All'))
filters.append(PatientFilter('Assigned', 'service__isnull', False))
filters.append(PatientFilter('Unassigned', 'service__isnull', True))
self.fields["filter"].choices = [(i, f.name) for i, f in enumerate(filters)]
As answered above, you need to initialize filters as an instance variable:
def __init__(...):
self.filters = []
self.filters.append(...)
# ...
If you want to know more about how the Form class works, you should read this page in the Django wiki:
Model Creation and Initialization
It talks about the internals of the Model class, but you'll find the general setup of fields is somewhat similar to the Form (minus the database stuff). It's a bit dated (2006), but I think the basic principles still apply. The metaclass stuff can be a bit confusing if you're new though.
Related
In the project I'm assigned to we use pytransitions. Our states are created, get equipped with additional attributes and added to a list one by one as objects first. Then this list of State objects is passed to a Machine object.
Here's a simple example:
from transitions import State
states = []
state_initial = State("initial", on_exit="some_callback")
text = "this is some text"
state.text = text
states.append(state)
This is how a machine is created:
from transitions import Machine
from some_library import SomeClass
from my_module import user_transitions
class User:
states = states
initial_state = states[0]
def __init__(self, some_param: str, another_param: SomeClass = default_param):
self.machine = Machine(model=self,
states=User.states,
initial=User.initial_state,
transitions=user_transitions,
prepare_event="preparing_callback",
after_state_change="ending_callback")
What I'd like to do is to add tags to my states at the time of or after state object creation. I mean the tags in transitions.extensions.states, so I could get them with is_tag kind of methods like in docs. Something like state_initial.add_tags(["tag1", "tag2"]) or
state_initial = State("initial", on_exit="some_callback", tags=["tag1", "tag2"])
or in any other way considering my legacy setup. How do I go about this?
My first suggestion would be to check whether you can streamline the state creation process by using a dedicated TextState instead of just assigning an additional attribute. This way you can keep your state configuration a bit more comprehensible. Reading machine configurations from yaml or json files gets way easier as well.
from transitions import Machine, State
from transitions.extensions.states import Tags
# option a) create a custom state class and use it by default
# class TextState and CustomState could be combined of course
# splitting CustomState into two classes decouples tags from the
# original state creation code
class TextState(State):
def __init__(self, *args, **kwargs):
self.text = kwargs.pop('text', '')
super(TextState, self).__init__(*args, **kwargs)
class CustomState(Tags, TextState):
pass
class CustomMachine(Machine):
state_cls = CustomState
states = []
state_initial = CustomState("initial", text="this is some text")
# we can pass tags for initialization
state_foo = dict(name="foo", text="bar!", tags=['success'])
states.append(state_initial)
states.append(state_foo)
# [...] CustomMachine(model=self, states=User.states, initial=User.initial_state)
But your question was about how you can inject tag capability AFTER states have been created. Probably because it would need major refactoring and deep digging to alter state creation. Adding state.tags = ['your', 'tags', 'here'] is fine and should work out of the box for graph and markup creation. To get state.is_<tag> working you can alter its __class__ attribute:
from transitions import Machine, State
from transitions.extensions.states import Tags
# option b) patch __class__
states = []
state_initial = State("initial")
state_initial.text = "this is some text"
# we can pass tags for initialization
state_foo = State("foo")
state_foo.text = "bar!"
state_foo.tags = ['success']
states.append(state_initial)
states.append(state_foo)
# patch all states
for s in states:
s.__class__ = Tags
s.tags = []
# add tag to state_foo
states[1].tags.append('success')
class User:
states = states
initial_state = states[0]
def __init__(self):
self.machine = Machine(model=self,
states=User.states,
initial=User.initial_state)
user = User()
user.to_foo()
assert user.machine.get_state(user.state).is_success # works!
assert not user.machine.get_state(user.state).is_superhero # bummer...
But again, from my experience code becomes much more comprehensible and reusable when you strive to separate machine configuration from the rest of the code base. Patching states somewhere in the code and assigning custom paramters might be overlooked by the next guy working with your code and it surely is surprising when states change their class between two debugging breakpoints.
So I have a ModelForm with a select field. In this select field there are about 100 entries. Of these entries I have a number of entries like "Organization Colorado - Denver", which I would like to have at the top of the list, such that all the entries with "Organization Colorado" are at the top of the list, and everything else is sorted in lexicographical order.
I've tried making two separate querysets (this seems like a bad idea, but manageable with only 100 or so entries). There seems to be a lot of ways of combining these two query sets, but without maintaining the order (which is the point). I've tried this:
class CreateContactForm(ModelForm):
...
def __init__(self, *args, **kwargs):
super(CreateContactForm, self).__init__(*args, **kwargs)
p = models.ConstantContactList.objects.filter(
name__startswith=settings.PREF_ORGANIZATION_PREFIX
)
np = models.ConstantContactList.objects.filter(
name__regex=r'^(?!{})'.format(settings.PREF_ORGANIZATION_PREFIX)
).order_by('-name')
self.fields['cc_lists'].queryset = list(p) + list(np)
This doesn't work, although it might, if there was some way to convert that list back into a queryset, or if there is a way to go around the queryset maybe? I'm not sure. Can anyone provide a clue as to what I should do?
I would recommend against trying to order the querysets and just handle sorting in the rendering layer (templates or forms). This way if you want to localize your code you won't have to change your queries.
Assuming you use a forms.Select widget. You may want to inherit from this widget and override the render_menu logic in order to construct it yourself and handle the ordering yourself. You'll have access to the rendered or unrendered options, so it shouldn't be an issues from that point on.
OK. I've come up with a solution that works here, and I'm providing it to anyone else who has the same need.
from django.forms.widgets import Select
import re
class CustomOrderingWidget(Select):
def __init__(self, priority_regex, sort='+', attrs=None):
super(CustomOrderingWidget, self).__init__(attrs)
self.regex = re.compile(priority_regex)
self.sort = sort
self.template_name = 'django/forms/widgets/select.html'
def render(self, name, value, attrs=None, renderer=None):
context = self.get_context(name, value, attrs)
optgroups = context.get('widget').get('optgroups')
firsts, others = [], []
for grp in optgroups:
if self.regex.search(grp[1][0].get('label')):
firsts.append(grp)
else:
others.append(grp)
if self.sort == '+':
kfn = lambda x: x[1][0].get('label')
context['widget']['optgroups'] = sorted(firsts, key=kfn) +\
sorted(others, key=kfn)
elif self.sort == '-':
kfn = lambda x: x[1][0].get('label')
context['widget']['optgroups'] =\
sorted(firsts, key=kfn, reverse=True) +\
sorted(others, key=kfn, reverse=True)
else:
context['widget']['optgroups'] = firsts + others
return self._render(self.template_name, context, renderer)
Then you can plug it into a ModelForm like this...
import settings # YOUR personal stuffz!
class CreateContactForm(ModelForm):
...
class Meta:
...
widgets = {
# Just an example, make your own regex string!
'cc_lists': CustomOrderingWidget("^{0}".format(
settings.PREF_ORGANIZATION_PREFIX
))
}
For my project I need to dynamically create custom (Class) methods.
I found out it is not so easy in Python:
class UserFilter(django_filters.FilterSet):
'''
This filter is used in the API
'''
# legacy below, this has to be added dynamically
#is_field_type1 = MethodFilter(action='filter_field_type1')
#def filter_field_type1(self, queryset, value):
# return queryset.filter(related_field__field_type1=value)
class Meta:
model = get_user_model()
fields = []
But it is giving me errors (and headaches...). Is this even possible?
I try to make the code between #legacy dynamic
One option to do this I found was to create the class dynamically
def create_filter_dict():
new_dict = {}
for field in list_of_fields:
def func(queryset, value):
_filter = {'stableuser__'+field:value}
return queryset.filter(**_filter)
new_dict.update({'filter_'+field: func})
new_dict.update({'is_'+field: MethodFilter(action='filter_'+field)})
return new_dict
meta_model_dict = {'model': get_user_model(), 'fields':[]}
meta_type = type('Meta',(), meta_model_dict)
filter_dict = create_filter_dict()
filter_dict['Meta'] = meta_type
UserFilter = type('UserFilter', (django_filters.FilterSet,), filter_dict)
However, this is giving me
TypeError at /api/v2/users/
func() takes 2 positional arguments but 3 were given
Does anyone know how to solve this dilemma?
Exception Value: 'UserFilter' object has no attribute 'is_bound'
You are getting this error because the class methods you are generating, are not bound to any class. To bound them to the class, you need to use setattr()
Try this on a console:
class MyClass(object):
pass
#classmethod
def unbound(cls):
print "Now I'm bound to ", cls
print unbound
setattr(MyClass, "bound", unbound)
print MyClass.bound
print MyClass.bound()
Traceback:
UserFilter = type('Foo', (django_filters.FilterSet, ), create_filter_dict().update({'Meta':type('Meta',(), {'model':
get_user_model(), 'fields':[]} )})) TypeError: type() argument 3 must
be dict, not None
Now, this is failing because dict.update() doesn't return the same instance, returns None. That can be fixed easily
class_dict = create_filter_dict()
class_dict.update({'Meta':type('Meta',(), {'model': get_user_model(), 'fields':[]})}
UserFilter = type('Foo', (django_filters.FilterSet, ), class_dict))
However, just look how messy that code looks. I recommend to you to try to be
clearer with the code you write even if it requires to write a few extra lines. In the long run, the code will be easier to maintain for you and your team.
meta_model_dict = {'model': get_user_model(), 'fields':[]}
meta_type = type('Meta',(), meta_model_dict)
filter_dict = create_filter_dict()
filter_dict['Meta'] = meta_type
UserFilter = type('Foo', (django_filters.FilterSet,), filter_dict)
This code might not be perfect but it is more readable than the original line of code you posted:
UserFilter = type('Foo', (django_filters.FilterSet, ), create_filter_dict().update({'Meta':type('Meta',(), {'model': get_user_model(), 'fields':[]})}))
And removes a complication on an already kinda difficult concept to grasp.
You might want to learn about metaclasses. Maybe you can overwrite the new method of a class. I can recommend you 1 or 2 posts about that.
Another option is that maybe you are not adding the filters correctly or in a way django doesn't expect? That would explain why you get no errors but none of your functions gets called.
You can use classmethod. Here is example how you can use it:
class UserFilter:
#classmethod
def filter_field(cls, queryset, value, field = None):
# do somthing
return "{0} ==> {1} {2}".format(field, queryset, value)
#classmethod
def init(cls,list_of_fields ):
for field in list_of_fields:
ff = lambda cls, queryset, value, field=field: cls.filter_field(queryset, value, field )
setattr(cls, 'filter_'+field, classmethod( ff ))
UserFilter.init( ['a','b'] )
print(UserFilter.filter_a(1,2)) # a ==> 1 2
print(UserFilter.filter_b(3,4)) # b ==> 3 4
You are asking for:
custom (Class) methods.
So we take an existing class and derive a subclass where you can add new methods or overwrite the methods of the original existing class (look into the code of the original class for the methods you need) like this:
from universe import World
class NewEarth(World.Earth):
def newDirectionUpsideDown(self,direction):
self.rotationDirection = direction
All the other Methods and features of World.Earth apply to NewEarth only you can now change the direction to make the world turn your new way.
To overwrite an existing method of a class is as as easy as this.
class NewEarth(World.Earth):
def leIitRain(self,amount): # let's assume leIitRain() is a standard-function of our world
return self.asteroidStorm(amount) #let's assume this is possible Method of World.Earth
So if someone likes a cool shower on earth he/she/it or whatever makes room for new development on the toy marble the burning way.
So have fun in your way learning python - and don't start with complicated things.
If I got you completely wrong - you might explain your problem in more detail - so more wise people than me can share their wisdom.
Hello Stack Overflow!
I am executing a simple command in a program that compiles a report of all the books contained in a library. The library contains a list of shelves, each shelves contains a dictionary of books. However, despite my best efforts, I am always duplicating all my books and placing them on every shelf, instead of the shelf I've instructed the program to place the book on.
I expect I have missed out on some kind of fundamental rule with object creation and organization.
I believe the culprits are the enshelf and unshelf methods in the book class.
Thank you so much for your time,
Jake
Code below:
class book():
shelf_number = None
def __init__(self, title, author):
super(book, self).__init__()
self.title = title
self.author = author
def enshelf(self, shelf_number):
self.shelf_number = shelf_number
SPL.shelves[self.shelf_number].books[hash(self)] = self
def unshelf(self):
del SPL.shelves[self.shelf_number].books[hash(self)]
return self
def get_title(self):
return self.title
def get_author(self):
return self.author
class shelf():
books = {}
def __init__(self):
super(shelf, self).__init__()
def get_books(self):
temp_list = []
for k in self.books.keys():
temp_list.append(self.books[k].get_title())
return temp_list
class library():
shelves = []
def __init__(self, name):
super(library, self).__init__()
self.name = name
def make_shelf(self):
temp = shelf()
self.shelves.append(temp)
def remove_shelf(shelf_number):
del shelves[shelf_number]
def report_all_books(self):
temp_list = []
for x in range(0,len(self.shelves)):
temp_list.append(self.shelves[x].get_books())
print(temp_list)
#---------------------------------------------------------------------------------------
#----------------------SEATTLE PUBLIC LIBARARY -----------------------------------------
#---------------------------------------------------------------------------------------
SPL = library("Seattle Public Library")
for x in range(0,3):
SPL.make_shelf()
b1 = book("matterhorn","karl marlantes")
b2 = book("my life","bill clinton")
b3 = book("decision points","george bush")
b1.enshelf(0)
b2.enshelf(1)
b3.enshelf(2)
print(SPL.report_all_books())
b1.unshelf()
b2.unshelf()
b3.unshelf()
OUTPUT:
[['decision points', 'my life', 'matterhorn'], ['decision points', 'my life', 'matterhorn'], ['decision points', 'my life', 'matterhorn']]
None
[Finished in 0.1s]
..instead of [["decision points"],["my life"],["matterhorn"]]
Use dict.pop() instead of del.
Add self.books = {} to shelf's __init__. Don't declare books outside of the __init__, because if you do so, all of the instances of that class are going to refer to the same thing. Instead, this makes each instance have its own dictionary, which is of course what you want since a book can't be in two shelves at once.
Do the same for library and its shelves and book and its shelf_number.
Pass a library instance as an argument to enshelf and unshelf. When you refer to SPL from within your objects' methods, Python finds that there is no local SPL defined, so it searches for one outside of the local scope; but if you were to try to assign something to SPL or do some other sort of mutative business, you would get an UnboundLocalError.
Bonuses:
class book(object), class shelf(object), and class library(object). (Won't fix your problem, but you should do that anyway.)
You don't need to hash the keys before using them, they will be hashed (if they are hashable, but if you're hashing them, then they are).
There is no need to call super() unless you are inheriting from something, in which case you can delegate a method call to a parent or sibling using it - but you aren't doing that.
get_books() can be implemented as nothing more than return [self.books[k].get_title() for k in self.books.iterkeys()]
Likewise for report_all_books(): return [shlf.get_books() for shlf in self.shelves]. Note that I am not iterating over the indices, but rather over the elements themselves. Try for c in "foobar": print(c) in the interactive shell if you want to see for yourself.
Objective:
Given something like:
stackoverflow.users['55562'].questions.unanswered()
I want it converted into the following:
http://api.stackoverflow.com/1.1/users/55562/questions/unanswered
I have been able to achieve that, using the following class:
class SO(object):
def __init__(self,**kwargs):
self.base_url = kwargs.pop('base_url',[]) or 'http://api.stackoverflow.com/1.1'
self.uriparts = kwargs.pop('uriparts',[])
for k,v in kwargs.items():
setattr(self,k,v)
def __getattr__(self,key):
self.uriparts.append(key)
return self.__class__(**self.__dict__)
def __getitem__(self,key):
return self.__getattr__(key)
def __call__(self,**kwargs):
return "%s/%s"%(self.base_url,"/".join(self.uriparts))
if __name__ == '__main__':
print SO().abc.mno.ghi.jkl()
print SO().abc.mno['ghi'].jkl()
#prints the following
http://api.stackoverflow.com/1.1/abc/mno/ghi/jkl
http://api.stackoverflow.com/1.1/abc/mno/ghi/jkl
Now my problem is I can't do something like:
stackoverflow = SO()
user1 = stackoverflow.users['55562']
user2 = stackoverflow.users['55462']
print user1.questions.unanswered
print user2.questions.unanswered
#prints the following
http://api.stackoverflow.com/1.1/users/55562/users/55462/questions/unanswered
http://api.stackoverflow.com/1.1/users/55562/users/55462/questions/unanswered/questions/unanswered
Essentially, the user1 and user2 refer to the same SO object, so it can't represent different users.
I have been thinking any pointers to do that would be helpful, because this additional level of functionality would make the API far more interesting.
IMHO, when you recreate a new stackoverflow object, you need to separate the arguments from old instance attributes with a deep copy
import copy
........
def __getattr__(self,key):
dict = copy.deepcopy(self.__dict__)
dict['uriparts'].append(key)
return self.__class__(**dict)
....
If you want more flexibility on the URI parts, an abstraction is needed for a cleaner design. For example:
class SOURIParts(object):
def __init__(self, so, uriparts, **kwargs):
self.so = so
self.uriparts = uriparts
for k,v in kwargs.items():
setattr(self,k,v)
def __getattr__(self,key):
return SOURIParts(self.so, self.uriparts+[key])
def __getitem__(self,key):
return self.__getattr__(key)
def __call__(self,**kwargs):
return "%s/%s"%(self.so.base_url,"/".join(self.uriparts))
class SO(object):
def __init__(self, base_url='http://api.stackoverflow.com/1.1'):
self.base_url = base_url
def __getattr__(self,key):
return SOURIParts(self, [])
def __getitem__(self,key):
return self.__getattr__(key)
I hope this helps.
You could override __getslice__(Python 2.7), or getitem()(Python3.x) and use a memorizing decorator so that if the slice you request (the userid) has already been looked up it would use cached results -- otherwise it could retrieve the results and populate the existing SO instance object.
However, I think a more OO way to solve the problem is make SO a pure lookup module that returns stack overflow user objects which would then have the deeper-digging lookups for profile details. But thats just me.