I want to incorporate a FormWizard to handle a long form. After researching, it seems that django-merlin is the best option since it manages the formwizard via sessions. Trying to incorporate it (as mentioned in the django wizard docs), however, results in an AttributeError: type object 'CreateWizard' has no attribute 'as_view'.
Here is what it looks like:
from merlin.wizards.session import SessionWizard
class StepOneForm(forms.Form):
year = forms.ChoiceField(choices=YEAR_CHOICES)
...
class StepTwoForm(forms.Form):
main_image = forms.ImageField()
...
class StepThreeForm(forms.Form):
condition = forms.ChoiceField(choices=CONDITION)
...
class CreateWizard(SessionWizard):
def done(self, form_list, **kwargs):
return HttpResponseRedirect(reverse('wizard-done'))
url:
url(r'^wizard/(?P<slug>[A-Za-z0-9_-]+)/$', CreateWizard.as_view([StepOneForm, StepTwoForm, StepThreeForm])),
Since the merlin docs are a little sparse, I chose to use the as_view() method as described in the original django form wizard docs, but it results in an AttributeError. How should I incorporate the merlin wizard in my urlconf? Thanks for your ideas!
This is the error and traceback that I get after updating based on #mVChr's answer and defining steps like this:
step_one = Step('step_one', StepOneForm())
Error and Traceback:
TypeError at / issubclass() arg 1 must be a class
Traceback:
File /lib/python2.7/django/core/handlers/base.py" in get_response
89. response = middleware_method(request)
File "/lib/python2.7/django/utils/importlib.py" in import_module
35. __import__(name)
File "/myproject/myproject/urls.py" in <module>
7. from myapp.forms import step_one, step_two, step_three, CreateWizard
File "/myproject/myapp/forms.py" in <module>
16. step_one = Step('step_one', StepOneForm())
File "/lib/python2.7/merlin/wizards/utils.py" in __init__
36. if not issubclass(form, (forms.Form, forms.ModelForm,)):
Exception Type: TypeError at /
Exception Value: issubclass() arg 1 must be a class
Although Im still getting an error, I feel closer to the solution thanks to #mVChr. Any ideas on how to solve this error are greatly appreciated! Thanks for any ideas!
Note: I do not know if this will work, I am just trying to help Nick B translate the docs with a specific example to get him closer to the proper solution. Please let me know if this works as-is and I will remove this comment.
From reading the docs it looks like you need to pass a list of Step objects directly to your SessionWizard subclass instantiation, as follows:
from merlin.wizards.session import SessionWizard
from merlin.wizards.utils import Step
class StepOneForm(forms.Form):
year = forms.ChoiceField(choices=YEAR_CHOICES)
...
step_one = Step('step-one', StepOneForm())
class StepTwoForm(forms.Form):
main_image = forms.ImageField()
...
step_two = Step('step-two', StepTwoForm())
class StepThreeForm(forms.Form):
condition = forms.ChoiceField(choices=CONDITION)
...
step_three = Step('step-three', StepThreeForm())
class CreateWizard(SessionWizard):
def done(self, form_list, **kwargs):
return HttpResponseRedirect(reverse('wizard-done'))
And then in your urls.py:
url(r'^wizard/(?P<slug>[A-Za-z0-9_-]+)/$',
CreateWizard([step_one, step_two, step_three]))
Want to draw your attention that you use wrong syntax while making an object of Step.
This
step_one = Step('step-one', StepOneForm())
should be like
step_one = Step('step-one', StepOneForm)
You have to correct it in all step object.
Related
This is a bit complicated because I'm debugging some code written a long time ago in python 2.7
In progress of migrating to Python 3 (I know, I know) and facing this problem when trying to fix unit tests
The problem is I'm getting an error TypeError: object() takes no parameters
I'll list the functions below. I have to replace a lot of names of functions and objects. If you see an inconsistency in module names, assume it's typo.
First the class it's calling
class Parser(object):
def __init__(self, some_instance, some_file):
self._some_instance = some_instance
self.stream = Parser.formsomestream(some_file)
self.errors = []
#staticmethod
def formsomestream(some_file):
# return a stream
class BetterParser(Parser):
def parse(self):
# skip some steps, shouldn't relate to the problem
return details # this is a string
class CSVUploadManager(object):
def __init__(self, model_instance, upload_file):
self._model_instance = model_instance
self._upload_file = upload_file
# then bunch of functions here
# then.....
def _parse(self):
parser_instance = self._parser_class(self._model_instance, self._upload_file)
self._csv_details = parser_instance.parse()
# bunch of stuff follows
def _validate(self):
if not self._parsed:
self._parse()
validator_instance = self._validator_class(self._model_instance, self._csv_details)
# some attributes to set up here
def is_valid(self):
if not self._validated:
self._validate()
Now the test function
from somewhere.to.this.validator import MockUploadValidator
from another.place import CSVUploadManager
class TestSomething(SomeConfigsToBeMixedIn):
#mock.patch('path.to.BetterParser.parse')
#mock.patch('path.to.SomeValidator.__new__')
#mock.patch('path.to.SomeValidator.validate')
def test_validator_is_called(self, mock_validator_new, mock_parse):
mock_validator_new.return_value = MockUploadValidator.__new__(MockUploadValidator)
mock_parse.return_value = mock_csv_details
mock_validator_new.return_value = MockUploadValidator()
string_io = build_some_string_io_woohoo() # this returns a StringIO
some_file = get_temp_from_stream(string_io)
upload_manager = CSVUploadManager(a_model_instance, some_file)
upload_manager.is_valid() # this is where it fails and produces that error
self.assertTrue(mock_parse.called)
self.assertTrue(mock_validator_new.called)
validator_new_call_args = (SomeValidator, self.cash_activity, mock_csv_details)
self.assertEqual(mock_validator_new._mock_call_args_list[0][0], validator_new_call_args)
As you can see, the CSVUploadManager takes in the a django model instance and a file-like obj, this thing will trigger self._parser_class which calls BetterParser, then BetterParser does its things.
However, I'm guessing it's due to the mock, it returns TypeError: object() takes no parameters
My questions:
Why would this error occur?
Why only happening on python 3.x? (I'm using 3.6)
This also causes other tests (in different testcases) to fail when they would normally pass if I don't test them with the failed test. Why is that?
Is it really related to mocking? I'd assume it is because when I test on the server, the functionality is here.
EDIT: adding Traceback
Traceback (most recent call last):
File "/path/to/lib/python3.6/site-packages/mock/mock.py", line 1305, in patched
return func(*args, **keywargs)
File "/path/to/test_file.py", line 39, in test_validator_is_called:
upload_manager.is_valid()
File "/path/to/manager.py", line 55, in is_valid
self._validate()
File "/path/to/manager.py", line 36, in _validate
validator_instance = self._validator_class(self._model_instance, self._csv_details)
TypeError: object() takes no parameters
There should be 3 mock arguments, except self.
Like this:
#mock.patch('path.to.BetterParser.parse')
#mock.patch('path.to.SomeValidator.__new__')
#mock.patch('path.to.SomeValidator.validate')
def test_validator_is_called(self, mock_validate, mock_validator_new, mock_parse):
...
I am working on a Django template associated with a particular model. Within the view for this template, I am trying to access a record from a different model (Terms containing scientific terms), based on the parameter I would pass in the template.
I tried being using get_context_data to query a random term in the database, and then use a custom filter tag to replace the term to the one I want from within the template. Django is smarter, though and wouldn't let me do that.
Currently, I am trying to define the context within my views.py
class ArticlesView(DetailView):
model = models.Articles
template_name = 'web/articles-details.html'
context_object_name = 'Articles_details'
def get_context_data(self, *args, **kwargs):
ctx = super(ArticlesView, self).get_context_data(*args, **kwargs)
ctx['featured'] = Articles.objects.filter(featured=True)
ctx['term','arg'] = Articles.objects.filter('slug'=='arg')
return ctx
In the above code, the 'featured' context works fine, but not the 'term' one. It is clearly wrong; I know that... but I can't figure out what the correct syntax would be, and how I would provide the parameter from within the template. (I am trying to print out just the slug of the 'scientific term' in this example).
Any thoughts?
I know that I can set a ForeignKey within my models to connect them. The problem with that is that I would have at least 4-5 'scientific terms' on any given page, and therefore, I would have to add at least 5 related terms for each page, and then add them in a particular manner... a lot of repetition and messy.
Thanks for your assistance, in advance.
What you're doing with the context won't quite work as you perhaps think. To illustrate what you've done with that context dictionary;
>>> context = dict()
>>> context['slug', 'arg'] = 'test'
>>> context
{('slug', 'arg'): 'test'}
>>> context['slug']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'slug'
>>> context['arg']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'arg'
>>> context['slug', 'arg']
'test'
So in your template if you did something like {{ slug, arg }} you'd get your object, but I don't think that's valid in the template syntax.
So based on what you've got I suspect you'd want to do something like;
arg = 'my-slug'
ctx['term'] = Articles.objects.filter(slug=arg)
ctx['arg'] = arg
That would give you the arg used to match the slug on the Articles model with the queryset assigned to term.
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 am trying to create a REST API with Neo4j and Django in the backend.
The problem is that even when I have Django models using Neo4Django , I can't use frameworks like Tastypie or Piston that normally serialize models into JSON (or XML).
Sorry if my question is confusing or not clear, I am newbie to webservices.
Thanks for you help
EDIT: So I started with Tastypie and followed the tutorial on this page http://django-tastypie.readthedocs.org/en/latest/tutorial.html. I am looking for displaying the Neo4j JSON response in the browser, but when I try to access to http://127.0.0.1:8000/api/node/?format=json I get this error instead:
{"error_message": "'NoneType' object is not callable", "traceback": "Traceback (most recent call last):\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 217, in wrapper\n response = callback(request, *args, **kwargs)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 459, in dispatch_list\n return self.dispatch('list', request, **kwargs)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 491, in dispatch\n response = method(request, **kwargs)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 1298, in get_list\n base_bundle = self.build_bundle(request=request)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 718, in build_bundle\n obj = self._meta.object_class()\n\nTypeError: 'NoneType' object is not callable\n"}
Here is my code :
api.py file:
class NodeResource (ModelResource): #it doesn't work with Resource neither
class meta:
queryset= Node.objects.all()
resource_name = 'node'
urls.py file:
node_resource= NodeResource()
urlpatterns = patterns('',
url(r'^api/', include(node_resource.urls)),
models.py file :
class Node(models.NodeModel):
p1 = models.StringProperty()
p2 = models.StringProperty()
I would advise steering away from passing Neo4j REST API responses directly through your application. Not only would you not be in control of the structure of these data formats as they evolve and deprecate (which they do) but you would be exposing unnecessary internals of your database layer.
Besides Neo4Django, you have a couple of other options you might want to consider. Neomodel is another model layer designed for Django and intended to act like the built-in ORM; you also have the option of the raw OGM layer provided by py2neo which may help but isn't Django-specific.
It's worth remembering that Django and its plug-ins have been designed around a traditional RDBMS, not a graph database, so none of these solutions will be perfect. Whatever you choose, you're likely to have to carry out a fair amount of transformation work to create your application's API.
Django-Tastypie allows to create REST APIs with NoSQL databases as well as mentioned in http://django-tastypie.readthedocs.org/en/latest/non_orm_data_sources.html.
The principle is to use tastypie.resources.Resource and not tastypie.resources.ModelResource which is SPECIFIC to RDBMS, then main functions must be redefined in order to provide a JSON with the desired parameters.
So I took the example given in the link, modified it and used Neo4j REST Client for Python to get an instance of the db and perform requests, and it worked like a charm.
Thanks for all your responses :)
Thanks to recent contributions, Neo4django now supports Tastypie out of the box! I'd love to know what you think if you try it out.
EDIT:
I've just run through the tastypie tutorial, and posted a gist with the resulting example. I noticed nested resources are a little funny, but otherwise it works great. I'm pretty sure the gents who contributed the patches enabling this support also know how to take care of nested resources- I'll ask them to speak up.
EDIT:
As long as relationships are specified in the ModelResource, they work great. If anyone would like to see examples, let me know.
Well my answer was a bit vague so I'm gonna post how a solved the problem with some code:
Assume that I want to create an airport resource with some attributes. I will structure this in 3 different files (for readability reasons).
First : airport.py
This file will contain all the resource attributes and a constructor too :
from models import *
class Airport(object):
def __init__ (self, iata, icao, name, asciiName, geonamesId, wikipedia, id, latitude, longitude):
self.icao = icao
self.iata = iata
self.name = name
self.geonamesId = geonamesId
self.wikipedia = wikipedia
self.id = id
self.latitude = latitude
self.longitude = longitude
self.asciiName = asciiName
This file will be used in order to create resources.
Then the second file : AirportResource.py:
This file will contain the resource attributes and some basic methods depending on which request we want our resource to handle.
class AirportResource(Resource):
iata = fields.CharField(attribute='iata')
icao = fields.CharField(attribute='icao')
name = fields.CharField(attribute='name')
asciiName = fields.CharField(attribute='asciiName')
latitude = fields.FloatField(attribute='latitude')
longitude = fields.FloatField(attribute='longitude')
wikipedia= fields.CharField(attribute='wikipedia')
geonamesId= fields.IntegerField(attribute='geonamesId')
class Meta:
resource_name = 'airport'
object_class = Airport
allowed_methods=['get', 'put']
collection_name = 'airports'
detail_uri_name = 'id'
def detail_uri_kwargs(self, bundle_or_obj):
kwargs = {}
if isinstance(bundle_or_obj, Bundle):
kwargs['id'] = bundle_or_obj.obj.id
else:
kwargs['id'] = bundle_or_obj.id
return kwargs
As mentioned in the docs, if we want to create an API that handle CREATE, GET, PUT, POST and DELETE requests, we must override/implement the following methods :
def obj_get_list(self, bundle, **kwargs) : to GET a list of objects
def obj_get(self, bundle, **kwargs) : to GET an individual object
def obj_create(self, bundle, **kwargs) to create an object (CREATE method)
def obj_update(self, bundle, **kwargs) to update an object (PUT method)
def obj_delete(self, bundle, **kwargs) to delete an object (DELETE method)
(see http://django-tastypie.readthedocs.org/en/latest/non_orm_data_sources.html)
Normally, in ModelResource all those methods are defined and implemented, so they can be used directly without any difficulty. But in this case, they should be customized according to what we want to do.
Let's see an example of implementing obj_get_list and obj_get :
For obj_get_list:
In ModelResource, the data is FIRSTLY fetched from the database, then it could be FILTERED according to the filter declared in META class ( see http://django-tastypie.readthedocs.org/en/latest/interacting.html). But I didn't wish to implement such behavior (get everything then filter), so I made a query to Neo4j given the query string parameters:
def obj_get_list(self,bundle, **kwargs):
data=[]
params= []
for key in bundle.request.GET.iterkeys():
params.append(key)
if "search" in params :
query= bundle.request.GET['search']
try:
results = manager.searchAirport(query)
data = createAirportResources(results)
except Exception as e:
raise NotFound(e)
else:
raise BadRequest("Non valid URL")
return data
and for obj_get:
def obj_get(self, bundle, **kwargs):
id= kwargs['id']
try :
airportNode = manager.getAirportNode(id)
airport = createAirportResources([airportNode])
return airport[0]
except Exception as e :
raise NotFound(e)
and finally a generic function that takes as parameter a list of nodes and returns a list of Airport objects:
def createAirportResources(nodes):
data= []
for node in nodes:
iata = node.properties['iata']
icao = node.properties['icao']
name = node.properties['name']
asciiName = node.properties['asciiName']
geonamesId = node.properties['geonamesId']
wikipedia = node.properties['wikipedia']
id = node.id
latitude = node.properties['latitude']
longitude = node.properties['longitude']
airport = Airport(iata, icao, name, asciiName, geonamesId, wikipedia, id, latitude, longitude)
data.append(airport)
return data
Now the third manager.py : which is in charge of making queries to the database and returning results :
First of all, I get an instance of the database using neo4j rest client framework :
from neo4jrestclient.client import *
gdb= GraphDatabase("http://localhost:7474/db/data/")
then the function which gets an airport node :
def getAirportNode(id):
if(getNodeType(id) == type):
n= gdb.nodes.get(id)
return n
else:
raise Exception("This airport doesn't exist in the database")
and the one to perform search (I am using a server plugin, see Neo4j docs for more details):
def searchAirport(query):
airports= gdb.extensions.Search.search(query=query.strip(), searchType='airports', max=6)
if len(airports) == 0:
raise Exception('No airports match your query')
else:
return results
Hope this will help :)
I have a ModelForm which is not working as expected. The method is_valid() returns True even for empty fields and moreover in the clenead_data I don't have the expected fields, but only a field which should not be there (since is not rendered by the form).
My scenario is something like this:
my form class:
class MyForm(ModelForm):
class Meta:
model = MyModel
# fields = ('one', 'two', 'three')
widgets = {
'fieldA': SelectDateWidget(),
'fieldB': forms.HiddenInput(),
# ...
}
fieldX = forms.ChoiceField(widget=forms.RadioSelect(),
choices=(...),
initial=A_VALUE)
fieldY = forms.ChoiceField(widget=forms.RadioSelect(),
choices=(...),
initial=A_VALUE)
the form is rendered in the page as expected and submitted using POST (all the data is submitted correctly). Then in my controller i have something like:
self.form = MyForm(request.POST)
and self.form.cleaned_data has only one field inside (it's a field belonging to the model but unexpected in the form) before invoking is_valid() and after too.
I also tried to declare specific "fields" providing the tuple in the Meta class and viceversa specifying undesired ones using "exclude" with no luck.
I know, this is very vague... but could you give me some suggestions?
ps. the same approach with ModelForm works perfectly in other section of my project :P
...moreover if I open the interactive shell (python manage.py shell) and I try the following:
f = MyForm({})
f.is_valid()
I get the following exception:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/davidezanotti/MyPythonEnv/lib/python2.7/site-packages/django/forms/forms.py", line 126, in is_valid
return self.is_bound and not bool(self.errors)
File "/Users/davidezanotti/MyPythonEnv/lib/python2.7/site-packages/django/forms/forms.py", line 117, in _get_errors
self.full_clean()
File "/Users/davidezanotti/MyPythonEnv/lib/python2.7/site-packages/django/forms/forms.py", line 272, in full_clean
self._clean_fields()
File "/Users/davidezanotti/MyPythonEnv/lib/python2.7/site-packages/django/forms/forms.py", line 290, in _clean_fields
value = getattr(self, 'clean_%s' % name)()
TypeError: 'NoneType' object is not callable
Fuuuuuuuuu!!!! O___O'
After 3 hours I realized that the problem was in the form's superclass! I have my own ModelForm which extends django's ModelForm in order to provide dynamic methods to render forms using Bootstrap templates (http://twitter.github.io/bootstrap/base-css.html#forms).
So in my custom model I have a allFieldsGroupedInDivs method which is similar to "as_table" but use divs with specific classes to display data... and in order to provide more flexibility for complex form (where I have several fields and I want to order them in particular way) I have recently implemented a magic method (__getattr__) in order to render a single form field using the signature thefieldGroupedInDivs (where "thefield" is the name of the field to render). The problem is that Django uses that magic method in order to dynamically create the various clean_field() so my data was not cleaned properly! I solved simply by invoking the super implementation when the requested method is not in the form *GroupedInDivs :)
In conclusion: PAY ATTENTION WHEN OVERRIDING DJANGO'S CLASSES!