I have a model like this
class User(model):
username = XXX
addr1 = xxx
def get_username(self):
return self.username + 'some message'
def get_addr1(self):
return self.addr1 + 'some string'
and code I want to iterate through each objects and if function with get_+field.name exists then call that method, otherwise return the field itself.
Is there a way to do this? Below is pseudo code:
for field in each_obj._fields.itervalues():
if get_+fieldname exists then:
return that function call
else:
return self.field.name
you can call hasattr(obj, 'get_'+fieldname) to know if the method is there but the best to do that is to actually override __getattr__ in your class and just let Python do the rest.
Take a look at how I fake python attributes here http://ilian.i-n-i.org/faking-attributes-in-python-classes/ and also check for the alternative solution in the comments.
for var in obj.__dict__:
try:
print getattr(obj, 'get_%s' %var)()
except(AttributeError):
print var
Related
I've got a class to perform all the SQL tasks in my code. The problem is when I call a method inside this class to update the database, the self.dbConn.commit() line is not saving the data.
What am I missing? I'm sure it's a newbie error but I can't find the problem.
If more code examples are needed I can provide them.
Cheers!
This is the class itself:
class dbActivities:
def __init__(self):
self.dbConn = my.connect("xxx","xxx","xxx","xxx")
self.dbCursor = self.dbConn.cursor()
def updateDB(self, sql):
try:
self.dbCursor.execute(sql)
self.dbConn.commit()
return True
except:
return False
And that's how I'm calling the method:
dbHandler.updateDB("UPDATE xxx SET token = {}, WHERE xxx = {}".format(xxx, xxx))
This is my first post on stackoverflow, so any criticisms about how I ask the question are welcome.
In my code, I get this error:
RuntimeError: maximum recursion depth exceeded
Here is the code (the content is irrelevant, I just recreated the error in the simplest way possible). Basically I am trying to override __init__. I want to do something if the object is in the database and something else if it is not.
class Question(models.Model):
text = models.CharField(max_length=140)
asked = models.BooleanField(default=False)
def __init__(self, text, *args):
#called the __init__ of the superclass.
super(Question, self).__init__()
self, c = Question.objects.get_or_create(text=text)
if c:
print 'This question will be asked!'
self.asked = True
self.save()
else:
print 'This question was already asked'
assert self.asked == True
The error occurs when calling the constructor:
Question('how are you?')
I understand that the problem comes from the get_or_create method. Looking at the error message,
---> 12 self, c = Question.objects.get_or_create(text=text)
...
---> 146 return self.get_query_set().get_or_create(**kwargs)
...
---> 464 obj = self.model(**params)
get_or_create calls the constructor of the object at some point. Which then calls get_or_create again etc...
EDIT: What I would like to achieve is basically being able to write:
Question('How are you?')
and having the object returned if it's in the database, or the newly created (and saved) one if it's not. Instead of something like:
> try:
> q = Question.objects.get(text='How are you?')
> except Question.DoesNotExist:
> q = Question(text='How are you?')
> q.save()
So I guess that the only way to achieve this is by overriding the __init__. Isn't it possible or is it conceptually wrong (or both)? Thanks!
You shouldn't really try and to this in the __init__. (In fact, it's best to leave the __init__ of Django models well alone.) It should go in the form, or the view.
In any case, you can't overwrite an instance inside itself by assigning to self - that's just a local variable like any other and will go out of scope at the end of the method.
Also note that you can use the defaults parameter to get_or_create to pass default values to be set on the new instance if an existing one is not found:
question, created = Question.objects.get_or_create(text=text, defaults={'asked': True})
Edit after question update Your edit makes it even clearer that __init__ is really not the place to be doing this. Don't forget, even evaluating a normal queryset will instantiate model objects, which means calling __init__ - so just getting an instance from the database will run into problems. Don't do this.
Instead, if you really need this to be provided by the model - even though it's just one line of code, as shown above - you could define a classmethod:
class Question(models.Model):
...
#classmethod
def query(cls, text):
question, _ = cls.objects.get_or_create(text=text, defaults={'asked': True})
return question
Then you could do Question.query('How are you') and it would return either the new or the existing item.
The logic within your __init__ needs to be moved somewhere else, like to a view. get_or_create returns two values: 1) the object and 2) if the object had to be created or not. See the docs for more details.
def some_view(request):
c, created = Question.objects.get_or_create(text=text)
if not created:
print 'This question was already asked'
else:
print 'This question will be asked!'
c.asked = True
c.save()
Like #Scott Woodall said, you need to move you init logic.
that is What's happening:
When you call Question('how are you?'), you go to __init__ method.
The __init__ calls Question.objects.get_or_create(text=text), who does't find a Question with that text, so try to create a new Question, calling __init__ (again).
You are re-entering in the method call forever.
Question('how are you?') # <-- You call this method
|
+-- def __init__(self, text, *args):
|
+-- Question.objects.get_or_create(text=text) # This try to create a model, calling __init__ method!
|
+-- def __init__(self, text, *args):
|
+-- Question.objects.get_or_create(text=text) # This try to create a model, calling __init__ method!
|
+ # So on...
I think you should add a unique=True to text Question field.
See the #Scott answer
I have a form that asks the user to enter in their zip code. Once they do it sends them to another form where there is a field called 'pickup_date'. This gets the value of the zip from the previous field and gets all of the available pickup_dates that match that zip code into a ChoiceField. I set all of this within the init of the model form.
def __init__(self,*args,**kwargs):
super(ExternalDonateForm,self).__init__(*args,**kwargs)
if kwargs:
zip = kwargs['initial']['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
elif self.errors:
zip = self.data['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
The problem I have is when there are other errors on the form. I use the elif self.errors to regenerate the possible choices but it doesn't default to the original selected option. It goes back and defaults to the first choice. How can I make it so it's default option on form errors is what was originally posted?
Change self.fields['pickup_date'] to self.fields['pickup_date'].initial and see if that helps.
I got it to work after playing around for a while. Above, I was setting all the dynamic choices with a get_dates() function that returned a tuple. Instead of doing that I returned a field object like this using a customized ModelChoiceField instead of a regular ChoiceField....
class MyModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.date.strftime('%a %b %d, %Y')
Dates function
def get_dates(self,zip):
routes = Route.objects.filter(zip=zip).values_list('route',flat=True)
pickups = self.MyModelChoiceField(queryset = PickupSchedule.objects.filter(
current_count__lt=F('specials'),
route__in=routes,
).order_by('date')
)
if not pickups:
pickups = (('----','No Pickups Available At This Time'),)
return pickups
in the init i set the value for self.fields['pickup_date'] like so..
self.fields['pickup_date'] = self.get_dates(zip)
I have a model with contains a get_slug definition:
def Specimen(models.Model):
...
def get_slug(self):
return '%s/%s-%d' % (slugify(self.longname),self.collection.collection_code, self.accessionnumber)
In my view I want to do this:
def show_detail(request):
specimens = Specimen.objects.filter(...)
specimen_data = []
for s in specimens:
specimen_tuple = (str(s.get_slug), format(s.latdecimal), format(s.longdecimal))
specimen_data.append(related_tuple)
context['specimen_data'] = simplejson.dumps(specimen_data)
But when I try to do something with the slug in js (though I have the same result in the shell) I find something like <bound method Specimen.get_slug of <Specimen: Specimen object>> instead of my slug.
How can I force the method to be evaluated prior to passing to JSON?
Any help much appreciated.
Try replacing s.get_slug with s.get_slug() so that you actually call your method.
I don't know if something like this is possbile but I would like to call a function based on the named group value in the urls.py config.
This is what I have been looking at
url.py snippet
url(r'^ajax/ValidateLogin/(?P<funcname>\w{13})/$', Validate),
views.py snippet
def checkUsername(request):
user = User.objects.get(username=request.POST.get('username',''))
if user == None:
response = simplejson.dumps({'success':'True','validate':'True'})
else:
response = simplejson.dumps({'success':'True','validate':'False'})
return HttpResponse(response,content_type='application/javascript; charset=utf-8')
def Validate(request,funcname):
return funcname(request)
This just returns that unicode object isn't callable which I understand but how do I then convert it to call the view function check username? An example of url that should call this is ajax/ValidateLogin/checkUsername
Thanks
remote_function_calls = {
'check_username' : check_username,
}
def Validate(request, funcname):
try:
return remote_function_calls[funcname](request)
except KeyError:
response = http.HttpResponse("Invalid function")
response.status_code = 403
return response
Another method to define which functions are valid
def valid_function(request):
return http.HttpResponse("This function may be called remotely")
valid_function.remote_call_valid = True
def erase_everything_function(request):
world.delete()
valid_functions = dict([(k, v) for k, v in globals().iteritems() if
getattr(v, 'remote_call_valid', None) == True])
Use this:
def Validate(request,funcname):
return locals()[funcname](request)
Although this kind of thing is a bit of a security risk since any user can call any arbitrary function available to python by passing the right "funcname".