Having trouble with the manager functionality in django - django

Designing code to handle transfers of objects called "contractBundles". Have this in the models.py in a "contract" app:
class contractBundleManager():
# def trade(self, buyer, seller, amount):
def add (self, user=pgamedb_models.Player, contractName=str, amount=float):
contractList = contract.objects.all()
for c in contractList:
if contractName == c.contractText:
user.money = user.money - (amount * c.value)
return self.create(contract=c, volume=amount, owner=user)
class contractBundle(models.Model):
objects = contractBundleManager()
contract = models.ForeignKey('contract')
volume = models.FloatField()
owner = models.ForeignKey(pgamedb_models.Player)
def __str__(self):
return '%10.2f x %s -> %s' % (self.volume, self.contract.shortName, self.owner.user.username)
Elsewhere, I want to let people purchase the bundles by pressing a button:
from contracts import models as cmodels
...
if request.method == 'POST':
...
elif 'Buy' in request.POST:
[set up activeUser, polName, amount]
cmodels.contractBundle.objects.add(user=activeUser, contractName=polName, amount=amount)
Yet when the code calls the contractBundle.objects.add() method, I get the following error:
AttributeError at [url]
'Manager' object has no attribute 'add'
Request Method: POST
Request URL: [url]
Django Version: 1.3.1
Exception Type: AttributeError
Exception Value:
'Manager' object has no attribute 'add'
Exception Location: ...views.py in [method], line 56
Python Executable: /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
Python Version: 2.7.1
Any thoughts why this might be happening?

There are a couple of things here that are wrong.
Firstly, your manager class needs to inherit from models.Manager:
class contractBundleManager(models.Manager):
This is where all the code like create is defined.
Also, it seems like a really bad idea to use classes/types as default values for function parameters. If you want to document what types a function needs, you should use a docstring.
Edit after comment No, I'm not referring to the return statement or the way you're calling self.create. I'm referring to the default values in the contractBundleManager.add method. You should never do this. If someone calls your method without passing the user parameter, the function will use the default value you have defined - which is a class. Classes in Python are mutable, so your function will actually modify the definition of the Player class.
Really, this is not the way to show the correct types to call the function with. Leave out the default values, and use a docstring.

#model
class contractBundle(models.Model):
objects = models.Manager( )
contact_bundle_manager = contractBundleManager( )
#view
cmodels.contact_bundle_manager.add(user=activeUser, contractName=polName, amount=amount)

Related

Django CBV - dealing with optional parameters in URLs

I have a Class Based View to list animals from a specific herd. There are multiple herds, so the user can either see all animals from ONE herd, or all animals from ALL herds.
How do I have an optional URL parameter and handle it in the CBV?
urls:
url(r'list/(?P<hpk>[0-9]+)/$', AnimalList.as_view(), name = 'animal_list'),
url(r'list/$', AnimalList.as_view(), name = 'animal_list'),
My view:
class AnimalList(ListView):
model = Animal
def get_queryset(self):
if self.kwargs is None:
return Animal.objects.all()
return Animal.objects.filter(herd = self.kwargs['hpk']) # <--- line 19 that returns an error
Going to a URL of like /animals/list/3/ works fine, while /animals/list/ fails with an error. Here's that error:
KeyError at /animals/list/
'hpk'
Request Method: GET
Request URL: http://localhost:8000/animals/list/
Django Version: 1.8.2
Exception Type: KeyError
Exception Value:
'hpk'
Exception Location: /var/www/registry/animals/views.py in get_queryset, line 19
I get that the self.kwargs is a dictionary, and when I print() it inside the view, it'll show it's empty. But I can't figure out how to capture that scenario. I feel like this is a simple, stupid error I'm missing.
To anyone who may stumble on this and need an answer, here is my working code after figuring it out:
class AnimalList(ListView):
model = Animal
def get_queryset(self):
if 'hpk' in self.kwargs:
return Animal.objects.filter(herd = self.kwargs['hpk'])
return Animal.objects.all()
Essentially we test to see if the URL parameter hpk is present in the list of self.kwargs. If it is, we filter the queryset. Otherwise, we return all animals.
Hope this helps someone :)
I would implement this using GET parameters instead of separate URLs. With this approach, there is only one URL /list/ that is filtered by parameters, for example /list/?hpk=1.
This is more flexible as you can eventually add more queries /list/?hpk=1&origin=europe
#url(r'list/$', AnimalList.as_view(), name = 'animal_list'),
class AnimalList(ListView):
model = Animal
def get_queryset(self):
queryset = Animal.objects.all()
hpk = self.request.GET.get("hpk"):
if hpk:
try:
queryset = queryset.filter(herd=hpk)
except:
# Display error message
return queryset

django tastypie: how do I control RelatedField "fullness" with url parameter?

I'm using django tastypie to publish a model with a Related (ToOne) field to another model resource. The uri is:
/api/map/?format=json
I want to let the client include a full_pages url parameter to get the full related page resource: /api/map/?full_pages=1&format=json
I don't really understand the Relationship Fields docs, but I made a get_full callable:
def get_full(bundle):
if bundle.request.GET.get('full_pages', 0):
return True
return False
I tried passing the callable to the full argument of ToOneField:
from tastypie.contrib.gis import resources as gis_resources
class MapResource(gis_resources.ModelResource):
page = fields.ToOneField('pages.api.PageResource', 'page', full=get_full)
But when I check with pdb, get_full is never invoked.
So then I tried creating a custom FillableToOneField with a full attribute:
class FillableToOneField(fields.ToOneFIeld):
full = get_full
class MapResource(ModelResource):
page = FillableToOneField('pages.api.PageResource', 'page')
Again, get_full is never invoked.
Is there a better, easier way to do this?
After reading Amyth's answer and django-boundaryservice code, I got this to work by defaulting full to True and altering it in the dehydrate method on the Related PageResource:
class MapResource(gis_resources.ModelResource):
page = fields.ToOneField('pages.api.PageResource', 'page', full=True)
pages.api:
class PageResource(ModelResource):
...
def dehydrate(self, bundle):
if not bundle.request.GET.get('full_pages'):
bundle = bundle.data['resource_uri']
return bundle
You can simply achieve this under the dehydrate method as follows.
class MapResource(ModelResource):
page = fields.ToOneField('pages.api.PageResource', 'page')
def dehydrate(self, bundle):
if bundle.request.Get.get('full_pages'):
self.page.full = True
return bundle
and have them send a request as /api/map/?full_pages=True&format=json

django form: Passing parameter from view.py to forms gives out error

Newbie question:
I need to accept a parameter in a form from a method in views.py but it gave me troubles. In the view I created a method with following snippet:
def scan_page(request):
myClient = request.user.get_profile().client
form = WirelessScanForm(client = myClient) # pass parameter to the form
and in the forms.py I defined the following form:
class WirelessScanForm(forms.ModelForm):
time = forms.DateTimeField(label="Schedule Time", widget=AdminSplitDateTime())
def __init__(self,*args,**kwargs):
myClient = kwargs.pop("client") # client is the parameter passed from views.py
super(WirelessScanForm, self).__init__(*args,**kwargs)
prob = forms.ChoiceField(label="Sniffer", choices=[ x.sniffer.plug_ip for x in Sniffer.objects.filter(client = myClient) ])
But django keeps giving me error saying: TemplateSyntaxError: Caught NameError while rendering: name 'myClient' is not defined(This error happens in the query)
I'm afraid it would be something stupid missing here, but I cannot really figure out why. Please help, thanks.
Assuming I've corrected your formatting properly, you have an indentation issue: prob is outside __init__, so doesn't have access to the local myClient variable.
However if you bring it inside the method, it still won't work, as there are two other issues: first, simply assigning a field to a variable won't set it on the form; and second, the choices attribute needs a list of 2-tuples, not just a flat list. What you need is this:
def __init__(self,*args,**kwargs):
myClient = kwargs.pop("client") # client is the parameter passed from views.py
super(WirelessScanForm, self).__init__(*args,**kwargs)
self.fields['prob'] = forms.ChoiceField(label="Sniffer", choices=[(x.plug_ip, x.MY_DESCRIPTIVE_FIELD) for x in Sniffer.objects.filter(client = myClient)])
Obviously replace MY_DESCRIPTIVE_FIELD with the actual field you want displayed in the choices.

Django-contact-forms: how to subclass and supply 'initial' parameter?

I have a Django form that is subclassed from the django-contact-form application. I want to supply an initial parameter to the form (which will vary depending on context).
This code returns the contact form fine, but obviously doesn't supply an initial parameter, so I need to extend it:
def contact_form(request):
scraper_form = scraperContactForm
return contact_views.contact_form(request=request, form_class=scraper_form)
This attempt to supply an initial parameter fails:
def contact_form(request):
scraper_form = scraperContactForm(initial={'title' : 'hello world'})
return contact_views.contact_form(request=request, form_class=scraper_form)
TypeError at /contact/
Keyword argument 'request' must be supplied
Hence, I have tried to supply a request argument, but weirdly, that fails by saying the form object is not callable:
def contact_form(request):
scraper_form = scraperContactForm(request=request, initial={'title' : 'hello world'})
# supply initial subject_dropdown field, if there's anything in the subject_type
return contact_views.contact_form(request=request, form_class=scraper_form)
TypeError at /contact/
'scraperContactForm' object is not callable
And no matter how I try to supply the request parameter, I keep on getting 'scraperContactForm' object is not callable.
FYI, this is the code for my subclassed form:
class scraperContactForm(ContactForm):
subject_dropdown = django.forms.ChoiceField(label="Subject type", choices=(('suggestion', 'Suggestion for improvement'), ('bug', 'Report a bug'), ('other', 'Other')))
title = django.forms.CharField(widget=django.forms.TextInput(), label=u'Subject')
recipient_list = [settings.FEEDBACK_EMAIL]
Please can anyone suggest what's going wrong?
You don't supply the full traceback in any of your examples. If you had, I suspect we would see that it's not your code that's giving the 'scraperContactForm' object is not callable error, but the subsequent call to the main contact_form view.
This is because that view is clearly expecting a form class, as indicated by the keyword parameters. However, you're already instantiating the form, by calling it in your first line, so you're actually passing a form instance, which isn't callable.
I would suggest that if you want to provide an initial value for a field, you do so in your subclassed form's definition:
class scraperContactForm(ContactForm):
title = django.forms.CharField(initial='hello world')

Django: Overriding the save() method: how do I call the delete() method of a child class

The setup =
I have this class, Transcript:
class Transcript(models.Model):
body = models.TextField('Body')
doPagination = models.BooleanField('Paginate')
numPages = models.PositiveIntegerField('Number of Pages')
and this class, TranscriptPages(models.Model):
class TranscriptPages(models.Model):
transcript = models.ForeignKey(Transcript)
order = models.PositiveIntegerField('Order')
content = models.TextField('Page Content', null=True, blank=True)
The Admin behavior I’m trying to create is to let a user populate Transcript.body with the entire contents of a long document and, if they set Transcript.doPagination = True and save the Transcript admin, I will automatically split the body into n Transcript pages.
In the admin, TranscriptPages is a StackedInline of the Transcript Admin.
To do this I’m overridding Transcript’s save method:
def save(self):
if self.doPagination:
#do stuff
super(Transcript, self).save()
else:
super(Transcript, self).save()
The problem =
When Transcript.doPagination is True, I want to manually delete all of the TranscriptPages that reference this Transcript so I can then create them again from scratch.
So, I thought this would work:
#do stuff
TranscriptPages.objects.filter(transcript__id=self.id).delete()
super(Transcript, self).save()
but when I try I get this error:
Exception Type: ValidationError
Exception Value: [u'Select a valid
choice. That choice is not one of the
available choices.']
... and this is the last thing in the stack trace before the exception is raised:
.../django/forms/models.py in save_existing_objects
pk_value = form.fields[pk_name].clean(raw_pk_value)
Other attempts to fix:
t =
self.transcriptpages_set.all().delete()
(where self = Transcript from the
save() method)
looping over t (above) and deleting each item individually
making a post_save signal on TranscriptPages that calls the delete method
Any ideas? How does the Admin do it?
UPDATE: Every once in a while as I'm playing around with the code I can get a different error (below), but then it just goes away and I can't replicate it again... until the next random time.
Exception Type:
MultiValueDictKeyError Exception
Value: "Key 'transcriptpages_set-0-id'
not found in "
Exception Location:
.../django/utils/datastructures.py in
getitem, line 203
and the last lines from the trace:
.../django/forms/models.py in _construct_form
form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs)
.../django/utils/datastructures.py in getitem
pk = self.data[pk_key]
In the end it was a matter of timing. When deleting only a single child object out of many, there was no problem. If I was deleting too many child objects at once, the error could happen, because the delete action was attempting to reference ids that were not around. This is why nothing worked, not signals, not [object]_set. I fixed it by using jquery to set a hidden variable in the edit form for the child object, which caused the object to first process the update (slowing down it's processing) and THEN the delete.
You are probably trying to access the Transaction.id before is has been created. Additionally, you can try to access the TransactionPage objects through the Transaction object via transactionpage_set (see query docs about FOO_set notation).
def save(self):
super(Transcript, self).save()
if self.doPagination:
self.transaction_set.all().delete() # .all() may be optional
pages = ... # whatever you do to split the content
self.numPages = len(pages)
self.save() # yes, a second save if you store numPages in Transaction
for page_number in range(self.numPages):
TransactionPage.objects.create(content=pages[page_number],
order=page_number, transaction=self)
You could also switch to not storing numPages in the Transaction and access it via a property instead.
class Transaction(models.Model):
# ...
# replace numPages with
#property
def page_count(self):
return self.transactionpage_set.count() or 1
And then if you took it one step further you could always use the TransactionPage objects for display purposes. This would allow you to get rid of the extra self.save() call in the above save() method. It will also let you simplify your templates by always displaying TransactionPage.content instead of conditionally displaying Transaction.body if you are paginating and TransactionPage.content otherwise.
class Transaction(models.Model):
body = models.TextField()
paginate = models.BooleanField()
#property
def page_count(self):
return self.transactionpage_set.count() # no more "or 1" cruft!
def save(self):
super(Transcript, self).save()
self.transaction_set.delete() # might need an .all() before .delete()
if self.paginate:
# Do whatever you do to split the body into pages.
pages = ...
else:
# The only page is the entire body.
pages = [self.body]
for (page_number, page_content) in enumerate(pages):
TransactionPage.objects.create(content=page_content,
order=page_number, transaction=self)
Another possible solution might be to override save_formset() in the ModelAdmin to prevent the update completely:
def save_formset(self, request, form, formset, change):
if (form.cleaned_data['do_pagination'] and
formset.model == TranscriptPages):
formset.changed_objects = []
formset.new_objects = []
formset.deleted_objects = []
else:
formset.save()
Then, you can do whatever you like in Modal.save(). Note that there's probably a more elegant/breakproof way to stop the formset from processing if one wanted to dig into the internals a bit more.