Prepopulate 1 form field that is a foreign key - python

Previously I was letting the user pick from a drop down of colors but I would like to rather pick one for them so I used the following code to determine which colors would be a valid choice and then randomly chose one. I'm trying to get it to pre-populate in the form and I'm getting a name error. Scratching my head like crazy because I tested this code by simply piping choice into a template already. So I know the code functioned properly in that context. Can I not do something that I've done below?
The error I'm getting when I launch my server is Name Error: name 'cur_colors' [at the list comprehension line] is not defined but it clearly is...
class LimitedJobForm(forms.ModelForm):
jobnum = forms.CharField(label='Job Number')
#get colorchoice
cur_jobs = Job.objects.filter(enddate__gte=(datetime.date.today()-timedelta(days=7)))
all_colors = Color.objects.all()
cur_colors = []
for i in cur_jobs:
cur_colors.append(i.color)
aval_colors = [x for x in all_colors if x not in cur_colors]
choice = random.choice(aval_colors)
color = forms.CharField(initial=choice)

You haven't defined an init method for this code to go into, thusly its just reading each line individually as a declaration
Move your code into an init method and it should work fine!
class LimitedJobForm(forms.ModelForm):
jobnum = forms.CharField(label='Job Number')
color = forms.CharField()
def __init__(self, *args, **kwargs):
super(LimitedJobForm, self).__init__(*args, **kwargs)
cur_jobs = Job.objects.filter(enddate__gte=(datetime.date.today()-timedelta(days=7)))
all_colors = Color.objects.all()
cur_colors = []
for i in cur_jobs:
cur_colors.append(i.color)
aval_colors = [x for x in all_colors if x not in cur_colors]
choice = random.choice(aval_colors)
self.fields['color'].initial = choice

Related

How to more quickly make a bunch of objects in Python

w_rook_1 = ChessPiece('w_rook_1')
w_knight_1 = ChessPiece('w_knight_1')
w_bishop_1 = ChessPiece('w_bishop_1')
w_king = ChessPiece('w_king')
w_queen = ChessPiece('w_queen')
w_bishop_2 = ChessPiece('w_bishop_2')
w_knight_2 = ChessPiece('w_knight_2')
w_rook_2 = ChessPiece('w_rook_2')
w_pawn_1 = ChessPiece('w_pawn_1')
w_pawn_2 = ChessPiece('w_pawn_2')
w_pawn_3 = ChessPiece('w_pawn_3')
w_pawn_4 = ChessPiece('w_pawn_4')
w_pawn_5 = ChessPiece('w_pawn_5')
w_pawn_6 = ChessPiece('w_pawn_6')
w_pawn_7 = ChessPiece('w_pawn_7')
w_pawn_8 = ChessPiece('w_pawn_8')
Is there an easier way to do this? I would also like to be able to use the objects afterwards.
Here is a simple approach using a dictionary when dealing with this type of challenge.
I added some comments within the code, please read.
instance_names = ['w_rook_1',
'w_knight_1',
'w_bishop_1',
'w_king',
'w_queen',
'w_bishop_2',
'w_knight_2',
'w_knight_2']
class ChessPiece(object):
def __init__(self, name):
self.name = name
self.move = "moving {}".format(name)
chess_objs = {}
for obj in instance_names:
# insert instance names ex. 'w_rook_1' as the key
# the ChessPiece instance is set as the value
chess_objs.setdefault(obj, ChessPiece(obj))
# here just illustrates how to access fields
# bound to each object
print(chess_objs['w_bishop_1'].name)
print(chess_objs['w_bishop_1'].move)
outputs:
w_bishop_1
moving w_bishop_1
If you follow #kaya3's advice and redesign your ChessPiece class, you could use a list comprehension easily, something like this (using abbreviations and ignoring number):
color = 'W'
non_pawns = [ChessPiece(color, c) for c in 'RNBKQBNR']
pawns = [ChessPiece(color, 'P') for _ in range(8)]

Creating Rest Api in python using JSON?

I'm new to python REST API and so facing some particular problems. I want that when I enter the input as pathlabid(primary key), I want the corresponding data assigned with that key as output. When I run the following code i only get the data corresponding to the first row of table in database even when the id i enter belong to some other row.
This is the VIEWS.PY
class pathlabAPI(View):
#csrf_exempt
def dispatch(self, *args, **kwargs):
# dont worry about the CSRF here
return super(pathlabAPI, self).dispatch(*args, **kwargs)
def post(self, request):
post_data = json.loads(request.body)
Pathlabid = post_data.get('Pathlabid') or ''
lablist = []
labdict = {}
lab = pathlab()
labs = lab.apply_filter(Pathlabid = Pathlabid)
if Pathlabid :
for p in labs:
labdict["Pathlabid"] = p.Pathlabid
labdict["name"] = p.Name
labdict["email_id"] = p.Emailid
labdict["contact_no"] = p.BasicContact
labdict["alternate_contact_no"] = p.AlternateContact
labdict["bank_account_number"] = p.Accountnumber
labdict["ifsccode"] = p.IFSCcode
labdict["country"] = p.Country
labdict["homepickup"] = p.Homepickup
lablist.append(labdict)
return HttpResponse(json.dumps(lablist))
else:
for p in labs:
labdict["bank_account_number"] = p.Accountnumber
lablist.append(labdict)
return HttpResponse(json.dumps(lablist))
There are a number of issues with the overall approach and code but to fix the issue you're describing, but as a first fix I agree with the other answer: you need to take the return statement out of the loop. Right now you're returning your list as soon as you step through the loop one time, which is why you always get a list with one element. Here's a fix for that (you will need to add from django.http import JsonResponse at the top of your code):
if Pathlabid:
for p in labs:
labdict["Pathlabid"] = p.Pathlabid
labdict["name"] = p.Name
labdict["email_id"] = p.Emailid
labdict["contact_no"] = p.BasicContact
labdict["alternate_contact_no"] = p.AlternateContact
labdict["bank_account_number"] = p.Accountnumber
labdict["ifsccode"] = p.IFSCcode
labdict["country"] = p.Country
labdict["homepickup"] = p.Homepickup
lablist.append(labdict)
else:
for p in labs:
labdict["bank_account_number"] = p.Accountnumber
lablist.append(labdict)
return JsonResponse(json.dumps(lablist))
As suggested in the comments, using Django Rest Framework or a similar package would be an improvement. As a general rule, in Django or other ORMs, you want to avoid looping over a queryset like this and adjusting each element. Why not serialize the queryset itself and do the logic that's in this loop in your template or other consumer?
You are return the response in for loop so that loop break on 1st entry
import json
some_list = []
for i in data:
some_list.append({"key": "value"})
return HttpResponse(json.dumps({"some_list": some_list}), content_type="application/json")
Try above example to solve your problem

Does django's Form class maintain state?

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.

Why is this a python syntax error during an initialisation?

This code:
class Todo:
def addto(self, list_name="", text=""):
"""
Adds an item to the specified list.
"""
if list_name == "":
list_name = sys.argv[2]
text = ''.join(sys.argv[3:]
todo_list = TodoList(getListFilename(list_name))
produces a syntax error with the little arrow pointing to todo_list on the last line.
The __init__ method for TodoList is here:
def __init__(self, json_location):
"""
Sets up the list.
"""
self.json_location = json_location
self.load()
I am kind of new to Python, so I don't see what I am doing wrong here.
you need to close this )
text = ''.join(sys.argv[3:]

Python classes from a for loop

I've got a piece of code which contains a for loop to draw things from an XML file;
for evoNode in node.getElementsByTagName('evolution'):
evoName = getText(evoNode.getElementsByTagName( "type")[0].childNodes)
evoId = getText(evoNode.getElementsByTagName( "typeid")[0].childNodes)
evoLevel = getText(evoNode.getElementsByTagName( "level")[0].childNodes)
evoCost = getText(evoNode.getElementsByTagName("costperlevel")[0].childNodes)
evolutions.append("%s x %s" % (evoLevel, evoName))
Currently it outputs into a list called evolutions as it says in the last line of that code, for this and several other for functions with very similar functionality I need it to output into a class instead.
class evolutions:
def __init__(self, evoName, evoId, evoLevel, evoCost)
self.evoName = evoName
self.evoId = evoId
self.evoLevel = evoLevel
self.evoCost = evoCost
How to create a series of instances of this class, each of which is a response from that for function? Or what is a core practical solution? This one doesn't really need the class but one of the others really does.
A list comprehension might be a little cleaner. I'd also move the parsing logic to the constructor to clean up the implemenation:
class Evolution:
def __init__(self, node):
self.node = node
self.type = property("type")
self.typeid = property("typeid")
self.level = property("level")
self.costperlevel = property("costperlevel")
def property(self, prop):
return getText(self.node.getElementsByTagName(prop)[0].childNodes)
evolutionList = [Evolution(evoNode) for evoNode in node.getElementsByTagName('evolution')]
Alternatively, you could use map:
evolutionList = map(Evolution, node.getElementsByTagName('evolution'))
for evoNode in node.getElementsByTagName('evolution'):
evoName = getText(evoNode.getElementsByTagName("type")[0].childNodes)
evoId = getText(evoNode.getElementsByTagName("typeid")[0].childNodes)
evoLevel = getText(evoNode.getElementsByTagName("level")[0].childNodes)
evoCost = getText(evoNode.getElementsByTagName("costperlevel")[0].childNodes)
temporaryEvo = Evolutions(evoName, evoId, evoLevel, evoCost)
evolutionList.append(temporaryEvo)
# Or you can go with the 1 liner
evolutionList.append(Evolutions(evoName, evoId, evoLevel, evoCost))
I renamed your list because it shared the same name as your class and was confusing.

Categories

Resources