I am writing an app on Google app engine using a zip of Django 1.0.2 because I am using appengine patch. (Yes, this is an old setup.)
I am trying to implement a MultiValueField and it's not working. As an example, I took exactly the code from http://www.hoboes.com/Mimsy/hacks/django-forms-edit-inline/multiwidgets-templates/ and put CMSChangeFields into my form as follows
# In models
class Foo(db.Model):
foo = db.StringProperty()
bar = db.StringProperty()
# In forms
class FooForm(djangoforms.ModelForm):
foo = fields.CMSChangeFields()
class Meta:
model = models.Foo
If I comment out foo in FooForm, I see two edit fields. If I leave foo in, I see "[[ UNDEFINED VARIABLE ]]", which is what I've set TEMPLATE_STRING_IF_INVALID to in my settings.py.
What is happening?
I see there may be a mismatch between Foo.foo and FooForm.foo types, but I'm not sure.
Alternatively, how do I debug this? I've been overriding init methods of the fields, forms, etc., and logging things, but I can't tell what's amiss.
If I try to use the debugger, it literally takes >10 minutes to start up, and uses the wrong source files (the google_appengine version of Django instead of my zip file), even though I've been over the project settings carefully. What I'm saying is the debugger is defying me.
If anyone ever runs across this, my problem was returning None from my widget decompress. Any value coming from decompress must have the same number of subscriptable values as widgets in the MultiWidget (in my case two). If foo is None, then foo[1] is bad. Unfortunately, Django 1.0.2 reported that problem with an empty value.
Related
I have created custom Django model-field subclasses based on CharField but which use to_python() to ensure that the model objects returned have more complex objects (some are lists, some are dicts with a specific format, etc.) -- I'm using MySQL so some of the PostGreSql field types are not available.
All is working great, but Pylint believes that all values in these fields will be strings and thus I get a lot of "unsupported-membership-test" and "unsubscriptable-object" warnings on code that uses these models. I can disable these individually, but I would prefer to let Pylint know that these models return certain object types. Type hints are not helping, e.g.:
class MealPrefs(models.Model):
user = ...foreign key...
prefs: dict[str, list[str]] = \
custom_fields.DictOfListsExtendsCharField(
default={'breakfast': ['cereal', 'toast'],
'lunch': ['sandwich']},
)
I know that certain built-in Django fields return correct types for Pylint (CharField, IntegerField) and certain other extensions have figured out ways of specifying their type so Pylint is happy (MultiSelectField) but digging into their code, I can't figure out where the "magic" specifying the type returned would be.
(note: this question is not related to the INPUT:type of Django form fields)
Thanks!
I had a look at this out of curiosity, and I think most of the "magic" actually comes for pytest-django.
In the Django source code, e.g. for CharField, there is nothing that could really give a type hinter the notion that this is a string. And since the class inherits only from Field, which is also the parent of other non-string fields, the knowledge needs to be encoded elsewhere.
On the other hand, digging through the source code for pylint-django, though, I found where this most likely happens:
in pylint_django.transforms.fields, several fields are hardcoded in a similar fashion:
_STR_FIELDS = ('CharField', 'SlugField', 'URLField', 'TextField', 'EmailField',
'CommaSeparatedIntegerField', 'FilePathField', 'GenericIPAddressField',
'IPAddressField', 'RegexField', 'SlugField')
Further below, a suspiciously named function apply_type_shim, adds information to the class based on the type of field it is (either 'str', 'int', 'dict', 'list', etc.)
This additional information is passed to inference_tip, which according to the astroid docs, is used to add inference info (emphasis mine):
astroid can be used as more than an AST library, it also offers some
basic support of inference, it can infer what names might mean in a
given context, it can be used to solve attributes in a highly complex
class hierarchy, etc. We call this mechanism generally inference
throughout the project.
astroid is the underlying library used by Pylint to represent Python code, so I'm pretty sure that's how the information gets passed to Pylint. If you follow what happens when you import the plugin, you'll find this interesting bit in pylint_django/.plugin, where it actually imports the transforms, effectively adding the inference tip to the AST node.
I think if you want to achieve the same with your own classes, you could either:
Directly derive from another Django model class that already has the associated type you're looking for.
Create, and register an equivalent pylint plugin, that would also use Astroid to add information to the class so that Pylint know what to do with it.
I thought initially that you use a plugin pylint-django, but maybe you explicitly use prospector that automatically installs pylint-django if it finds Django.
The checker pylint neither its plugin doesn't check the code by use information from Python type annotations (PEP 484). It can parse a code with annotations without understanding them and e.g. not to warn about "unused-import" if a name is used in annotations only. The message unsupported-membership-test is reported in a line with expression something in object_A simply if the class A() doesn't have a method __contains__. Similarly the message unsubscriptable-object is related to method __getitem__.
You can patch pylint-django for your custom fields this way:
Add a function:
def my_apply_type_shim(cls, _context=None): # noqa
if cls.name == 'MyListField':
base_nodes = scoped_nodes.builtin_lookup('list')
elif cls.name == 'MyDictField':
base_nodes = scoped_nodes.builtin_lookup('dict')
else:
return apply_type_shim(cls, _context)
base_nodes = [n for n in base_nodes[1] if not isinstance(n, nodes.ImportFrom)]
return iter([cls] + base_nodes)
into pylint_django/transforms/fields.py
and also replace apply_type_shim by my_apply_type_shim in the same file at this line:
def add_transforms(manager):
manager.register_transform(nodes.ClassDef, inference_tip(my_apply_type_shim), is_model_or_form_field)
This adds base classes list or dict respectively, with their magic methods explained above, to your custom field classes if they are used in a Model or FormView.
Notes:
I thought also about a plugin stub solution that does the same, but the alternative with "prospector" seems so complicated for SO that I prefer to simply patch the source after installation.
Classes Model or FormView are the only classes created by metaclasses, used in Django. It is a great idea to emulate a metaclass by a plugin code and to control the analysis simple attributes. If I remember, MyPy, referenced in some comment here, has also a plugin mypy-django for Django, but only for FormView, because writing annotations for django.db is more complicated than to work with attributes. - I was trying to work on it for one week.
I am very new to unit testing and am probably doing something wrong, but when I simulate a post to update a model via the admin backend it seems like my save_model method in my AdminForm isn't being called. I am trying to test this method - what am I doing wrong?
My second, less relevant question is in general how can I make sure a method is being called when I use unit testing? Is there some way to list all the methods that were hit?
Below is the code my test is running. In my save_model method in my AdminForm for this model, I set this model's foobar attribute to the username of the currently signed in user. Below is my test:
self.client = Client()
self.client.login(username='username',password='password')
# self.dict is a dictionary of field names and values for mymodel to be updated
response = self.client.post('/admin/myapp/mymodel/%d/' % self.mymodel.id, self.dict)
self.assertEqual(response.status_code,200) # passes
self.assertEqual(self.mymodel.foobar,'username') # fails
self.client.logout()
It fails because it says that self.mymodel.foobar is an empty string. That was what it should have been before the update. No value for foobar is passed in the self.dict but my save_model method is designed to set it on its own when the update happens. It is also worth noting that my code works correctly and save_model seems to work fine, just my test is failing. Since I am a total noob at TDD, I'm sure the issue is with my test and not my code. Thoughts?
From the code it looks like the problem is that, after posting the form, you don't reload self.mymodel from the database. If you hold a reference to a model object stored in the database, and one or more of the fields on that object is changed in the database, then you will need to reload the object from the database to see the updated values. As detailed in this question, you can do this with something like:
self.mymodel = MyModelClass.objects.get(id=self.mymodel.id)
To answer your second question, probably the most useful way to see what is happening would be to use logging to output what is happening in your save_model method - this will not only help you debug the issue during testing, but also if you encounter any issues in this method when running your application. The django guide to logging gives an excellent introduction:
https://docs.djangoproject.com/en/dev/topics/logging/
I am working on a project to expand a testing suite that my company uses. One of this things that was asked of me was to link the website to our Github source for code so that the dev team could continue tracking the issues there instead of trying to look in two places. I was able to do this but the problem is that every time the a bug is reported an issue is opened.
I want to add a field to my Django model that tracks an Issue object (from the github3.py wrapper) that is sent to Github. I want to use this to check if an Issue has already been created in Github by that instance of the BugReport and if it has been created, edit the Issue instead of creating another issue in Github that is a duplicate. Does Django have a something that can handle this sort of reference?
I am using Django 1.3.1 and Python 2.7.1
EDIT
I was able to figure my specific problem out using esauro's suggestions. However, as mkoistinen said, If this problem came up in a program where the work-around was not as easy as this one was, should an object reference be created like I had originally asked about or is that bad practice? and if it is okay to make an object reference like that, how would you do it with the Django models?
I'm the creator of github3.py.
If you want to get the issue itself via a number, there are a couple different ways to do this. I'm not sure how you're interacting with the API but you can do:
import github3
i = githbu3.issue('repo_owner', 'repo_name', issue_number)
or
import github3
r = github3.repository('repo_owner', 'repo_name')
i = r.issue(issue_number)
or
import github3
g = github3.login(client_key='client_key', client_secret='client_secret')
i = g.issue('repo_owner', 'repo_name', issue_number)
# or
r = g.repository('repo_owner', 'repo_name')
i = r.issue(issue_number)
otherwise if you're looking for something that's in an issue without knowing the number:
import github3
r = github3.repository('repo_owner', 'repo_name')
for i in r.iter_issues():
if 'text to search for' in i.body_text:
i.edit('...')
I followed this post here and sorted out how to set the ForeignKey of one model to a model in another application. However, when I try it a second time I get an error and not sure why.
I have Central app with models for a 'project' and an 'annotation', and a Reports app with a report model. An 'annotation' has a FK to a 'report' in the Reports app, and that seems to work fine with this code:
#models.py for Central app
from GIanno.pt_reports.models import Report
class annotation(models.Model):
...
report=models.ForeignKey(Report)
But, in the Reports app, when I try to set a FK for the 'report' to link it to a 'project' from the 'Central' app using the same format as above, I get an error "cannot import name 'project' from the import line.
Any ideas on why it works one way and not the other. Does order somehow matter? Thanks
My guess is that you have created a circular import condition. This occurs when you import something from one python module which in turns imports from the module which is trying to import it, thus preventing the import from ever resolving.
In general there are three strategies for dealing with circular imports, two of which will work in this case:
Move around your classes and imports so that the imports only go one direction.
Use lazy evaluation. In Django's case this can be accomplished for a ForeignKey by passing a string specifying the app name and model using dot notation: report=models.ForeignKey('central.Report')
Move the import statement out of the global module scope and into the scope of a function within the module. That way the import isn't evaluated immediately and the module can be successfully imported as a whole while still allowing the import within the module to happen when it's called. (Note: this won't work for ForeignKey relationships)
The lazy FK resolution (#2) is probably your best bet here. In general, though the best strategy is to simplify your model/module arrangement to avoid circular imports whenever possible.
Try:
class annotation(models.Model):
...
report=models.ForeignKey('centralapp.Report')
Replace 'centralapp' with name of your central app name without needing to import.
Lazy Relationships
Another scenario where the Lazy Relationships might be useful is with import order. It's not a circular reference (where it can't tell who's first) but a case where one piece of code is loaded before the other can be.
For example, let's say I have a Doc Model and a Log Model. The Log model has a FK for the Doc so I can record changes in the document. This works fine until, let's say, I try to generate a Log record in my save method for my Doc model (to make a save event log entry). There is no Log PK in the Doc object in this case but is a similar issue.
In this case you get an import order problem, where one will try to reference something that has not been loaded into Python yet. It's similar to a Circular Reference but a different cause.
This can be solved other ways but is another example where you will run into this problem.
This is really just a "best practices" question...
I find that When developing an app, I often end up with a lot of views.
Is it common practice to break these views up into several view files? In other words... instead of just having views.py, is it common to have views_1.py, views_2.py, views_3.py (but named more appropriately, perhaps by category)?
Splitting views.py
Most of your code probably expects your views to be accessible as myapp.views.viewname. One way I've seen people break up their views but keep this python name is to create a views/ directory. views/__init__.py will have:
from .foo_views import *
from .bar_views import *
from .baz_views import *
Then, in views/foo_views.py, put:
def foo_detail(request, ...):
# your code here
def foo_list(request, ...):
# your code here
def your_other_view(...):
# ...
etc. So you move everything from views.py into files in this directory, make __init__.py, delete views.py, and you're done.
Then, when you import myapp.views, myapp.views.foo_detail will refer to the function that you defined in views/foo_views.py.
Splitting other modules
This strategy should also work fine for admin.py, etc. But if you want to split up models.py like this, you will need to add app_label = 'your_app_name' to the class Meta: of all of your models. For example, unicorn_app/models/unicorns.py could have an entry like this:
class Unicorn(models.Model):
description = models.CharField(max_length=80)
class Meta:
app_label = 'unicorn_app'
(Otherwise, Django imagines that the Unicorn model is part of a Django app named "models", which messes up the admin site. Current through 1.6 - the upcoming 1.7 release will remove this requirement.)
As a general guideline, think about readability and maintainability: the default "views.py" is just a suggestion made by initial scaffolding - you do not have to stick to it.
Usually, files with thousands of lines of code are difficult to maintain, for this I usually try to decompose bigger modules into smaller ones.
On the other hand, the division should make sense - splitting related functions into several files, with lots of imports may make maintenance even more difficult.
Finally, you can also think about completely other ways to simplify your application.
Do you see duplicated code? Maybe some functionality could be moved in a completely different application? And so on.
Another option would be to move some of the functionality into one or more apps. This would allow you to move also forms and templates and keeping things structurized. You don't necessarily need to move the models which saves you from model and data migration.
For example you could have the following structure:
main_app/
|_models.py
|_views.py
|_forms.py
|_urls.py
|_templates/
sub_app_1/
|_views.py
|_forms.py
|_urls.py
|_templates/
sub_app_2/
|_views.py
|_forms.py
|_urls.py
|_templates/