Here is what I want to do:
Retrieve a list of all objects in all my django apps that inherit from forma.Form
Find all CharField defined on the form
Verify that each of these fields has a max_length kwarg specified
My goal is the write a unit test to fail if a form exists within our system that does not have a max length specified.
how would I do this?
I found a way to use reflection style code to retrieve all of the objects inheriting from the models.Form class, see test below:
class TestAllFormsMeetBasicBoundaryTests(TestCase):
def all_subclasses(self, cls):
return cls.__subclasses__() + [g for s in cls.__subclasses__()
for g in self.all_subclasses(s)]
# this is teh awesome-sause! dynamically finding objects in the project FTW!
def test_all_char_fields_contain_max_length_value(self):
from django.apps import apps
import django.forms
import importlib
log.info('loading all forms modules from apps')
at_least_one_module_found = False
for app_name in settings.PROJECT_APPS: #this is a list of in project apps
app = apps.get_app_config(app_name)
try:
importlib.import_module('{0}.forms'.format(app.label))
log.info('forms loaded from {0}'.format(app.label))
at_least_one_module_found = True
except ImportError as e:
pass
self.assertTrue(at_least_one_module_found, 'No forms modules were found in any of the apps. this should never be true!')
all_forms = self.all_subclasses(django.forms.Form)
messages = []
for form in all_forms:
for key in form.declared_fields.keys():
field = form.declared_fields[key]
if isinstance(field, django.forms.CharField) and form.__module__.partition('.')[0] in settings.PROJECT_APPS and field.max_length is None:
messages.append('{0}.{1}.{2} is missing a max_length value.'.format(form.__module__, form.__name__, key))
pass
self.assertEquals(0, len(messages),
'CharFields must always have a max_length attribute specified. please correct the following:\n--{0}'
.format('\n--'.join(messages)))
Related
I am using django-import-export 1.0.1 with admin integration in Django 2.1.1. I have two models
from django.db import models
class Sector(models.Model):
code = models.CharField(max_length=30, primary_key=True)
class Location(models.Model):
code = models.CharField(max_length=30, primary_key=True)
sector = ForeignKey(Sector, on_delete=models.CASCADE, related_name='locations')
and they can be imported/exported just fine using model resources
from import_export import resources
from import_export.fields import Field
from import_export.widgets import ForeignKeyWidget
class SectorResource(resources.ModelResource):
code = Field(attribute='code', column_name='Sector')
class Meta:
model = Sector
import_id_fields = ('code',)
class LocationResource(resources.ModelResource):
code = Field(attribute='code', column_name='Location')
sector = Field(attribute='sector', column_name='Sector',
widget=ForeignKeyWidget(Sector, 'code'))
class Meta:
model = Location
import_id_fields = ('code',)
and import/export actions can be integrated into the admin by
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
class SectorAdmin(ImportExportModelAdmin):
resource_class = SectorResource
class LocationAdmin(ImportExportModelAdmin):
resource_class = LocationResource
admin.site.register(Sector, SectorAdmin)
admin.site.register(Location, LocationAdmin)
For Reasons™, I would like to change this set-up so that a spreadsheet of Locations which does not contain a Sector column can be imported; the value of sector (for each imported row) should be taken from an extra field on the ImportForm in the admin.
Such a field can indeed be added by overriding import_action on the ModelAdmin as described in Extending the admin import form for django import_export. The next step, to use this value for all imported rows, is missing there, and I have not been able to figure out how to do it.
EDIT(2): Solved through the use of sessions. Having a get_confirm_import_form hook would still really help here, but even better would be having the existing ConfirmImportForm carry across all the submitted fields & values from the initial import form.
EDIT: I'm sorry, I thought I had this nailed, but my own code wasn't working as well as I thought it was. This doesn't solve the problem of passing along the sector form field in the ConfirmImportForm, which is necessary for the import to complete. Currently looking for a solution which doesn't involve pasting the whole of import_action() into an ImportMixin subclass. Having a get_confirm_import_form() hook would help a lot here.
Still working on a solution for myself, and when I have one I'll update this too.
Don't override import_action. It's a big complicated method that you don't want to replicate. More importantly, as I discovered today: there are easier ways of doing this.
First (as you mentioned), make a custom import form for Location that allows the user to choose a Sector:
class LocationImportForm(ImportForm):
sector = forms.ModelChoiceField(required=True, queryset=Sector.objects.all())
In the Resource API, there's a before_import_row() hook that is called once per row. So, implement that in your LocationResource class, and use it to add the Sector column:
def before_import_row(self, row, **kwargs):
sector = self.request.POST.get('sector', None)
if contract:
self.request.session['import_context_sector'] = sector
else:
# if this raises a KeyError, we want to know about it.
# It means that we got to a point of importing data without
# contract context, and we don't want to continue.
try:
sector = self.request.session['import_context_sector']
except KeyError as e:
raise Exception("Sector context failure on row import, " +
f"check resources.py for more info: {e}")
row['sector'] = sector
(Note: This code uses Django sessions to carry the sector value from the import form to the import confirmation screen. If you're not using sessions, you'll need to find another way to do it.)
This is all you need to get the extra data in, and it works for both the dry-run preview and the actual import.
Note that self.request doesn't exist in the default ModelResource - we have to install it by giving LocationResource a custom constructor:
def __init__(self, request=None):
super()
self.request = request
(Don't worry about self.request sticking around. Each LocationResource instance doesn't persist beyond a single request.)
The request isn't usually passed to the ModelResource constructor, so we need to add it to the kwargs dict for that call. Fortunately, Django Import/Export has a dedicated hook for that. Override ImportExportModelAdmin's get_resource_kwargs method in LocationAdmin:
def get_resource_kwargs(self, request, *args, **kwargs):
rk = super().get_resource_kwargs(request, *args, **kwargs)
rk['request'] = request
return rk
And that's all you need.
I have a Post class for my Django app that has several subclasses TextPost, AudioPost, etc, each with their own render_html() method.
class Post(models.Model):
author = models.ForeinKey(User,...)
title = models.CharField(...)
pub_date = models.DateTimeField(...)
...
def render_html(self):
return "rendered title, author date"
class AudioPost(Post):
audioFile = FileField(...)
def render_html(self):
return "Correct audio html"
...
each of these child models has an ModelForm with rules for uploading, validation, and saving.
In a home page view, I'd like to take all posts, arrange them by date, and render them. To me this should be as simple as
## in view
context = { 'posts' : Post.objects.order_by('-pub_date')[:5] }
and
## in template
{{ post.render_html() | safe }}
I remember things working this way for abstract classes in Java. But when I do it this way in Python, the render_html method gets called as if they are each members of the parent class. I've looked up how Django does multi-table inheritence, it seems like I either need to check the generated OneToOneFields one by one until I've found one that doesn't raise an exception, or use the InheritanceManager utility manager. Is one of those two ways the best way to do this or should I do something else?
I solved this via the following method inside Post, which allows me to do
{{ post.get_subclass().render_html() }}
inside the template. Assuming 4 subclasses, AudioPost, VideoPost, TextPost, and RichTextPost:
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
class Post(models.Model):
...
...
def get_subclass(self):
try:
textpost = self.textpost
return textpost
except ObjectDoesNotExist:
pass
try:
audiopost = self.audiopost
return audiopost
except ObjectDoesNotExist:
pass
try:
videopost = self.videopost
return videopost
except ObjectDoesNotExist:
pass
try:
richtextpost = self.richtextpost
return richtextpost
except ObjectDoesNotExist:
pass
raise ObjectDoesNotExist
I would suggest another method for your problem which you can use to get all the subclasses of the base class. It will be a bit of handy as you don't need to get query set for every child class manually
querysets_child = [child_cls.objects.all() for child_cls in vars()['BaseClass'].__subclasses__()]
The method you refer to which works on java but I don't think it can work here. Either you use child classes manually or get all child classes with subclass function mentioned above.
Let's say I've created a (hopefully) reusable app, fooapp:
urls.py
urls('^(?P<userid>\d+)/$', views.show_foo),
and fooapp's views.py:
def show_foo(request, userid):
usr = shortcuts.get_object_or_404(User, pk=userid)
... display a users' foo ...
return render_to_response(...)
Since it's a reusable app, it doesn't specify any access control (e.g. #login_required).
In the site/project urls.py, the app is included:
urls('^foo/', include('fooapp.urls')),
How/where can I specify that in this site only staff members should be granted access to see a user's foo?
How about if, in addition to staff members, users should be able to view their own foo (login_required + request.user.id == userid)?
I didn't find any obvious parameters to include..
Note: this has to do with access control, not permissions, i.e. require_staff checks User.is_staff, login_required checks if request.user is logged in, and user-viewing-their-own-page is described above. This question is in regards to how a site can specify access control for a reusable app.
Well, I've found a way that works by iterating over the patterns returned by Django's include:
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
def urlpatterns_iterator(patterns):
"""Recursively iterate through `pattern`s.
"""
_patterns = patterns[:] # create a copy
while _patterns:
cur = _patterns.pop()
if isinstance(cur, RegexURLPattern):
yield cur
elif isinstance(cur, RegexURLResolver):
_patterns += cur.url_patterns
else:
raise ValueError("I don't know how to handle %r." % cur)
def decorate(fn, (urlconf_module, app_name, namespace)):
"""Iterate through all the urls reachable from the call to include and
wrap the views in `fn` (which should most likely be a decorator).
(the second argument is the return value of Django's `include`).
"""
# if the include has a list of patterns, ie.: url(<regex>, include([ url(..), url(..) ]))
# then urlconf_module doesn't have 'urlpatterns' (since it's already a list).
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
for pattern in urlpatterns_iterator(patterns):
# the .callback property will set ._callback potentially from a string representing the path to the view.
if pattern.callback:
# the .callback property doesn't have a setter, so access ._callback directly
pattern._callback = fn(pattern._callback)
return urlconf_module, app_name, namespace
this is then used in the site/project's urls.py, so instead of:
urls('^foo/', include('fooapp.urls')),
one would do:
from django.contrib.admin.views.decorators import staff_member_required as _staff_reqd
def staff_member_required(patterns): # make an include decorator with a familiar name
return decorate(_staff_reqd, patterns)
...
urls('^foo/', staff_member_required(include('fooapp.urls'))),
I was trying to follow official doc of import-export:
https://django-import-export.readthedocs.org/en/latest/import_workflow.html#import-data-method-workflow
But I still do not know how to glue it to my admin assuming that:
I want only subset of fields (I created Resource model with listed fields, but It crashes while importing anyway with: KeyError full stack bellow.
Where - in which method - in my admin class (inheriting of course ImportExportModelAdmin and using defined resource_class) should i place the code responsible for some custom actions I want to happen after validating, that import data are correct but before inserting them into database.
I am not very advanced in Django and will be thankful for some hints.
Example of working implementation will be appreciated, so if you know something similar on github - share.
you can override it as
to create a new instance
def get_instance(self, instance_loader, row):
return False
your custom save
def save_instance(self, instance, real_dry_run):
if not real_dry_run:
try:
obj = YourModel.objects.get(some_val=instance.some_val)
# extra logic if object already exist
except NFCTag.DoesNotExist:
# create new object
obj = YourModel(some_val=instance.some_val)
obj.save()
def before_import(self, dataset, dry_run):
if dataset.headers:
dataset.headers = [str(header).lower().strip() for header in dataset.headers]
# if id column not in headers in your file
if 'id' not in dataset.headers:
dataset.headers.append('id')
hi i am using django app engine patch i have set up a simple model as follows
class Intake(db.Model):
intake=db.StringProperty(multiline=False, required=True)
##permerlink
def get_absolute_url(self):
return "/timekeeper/%s/" % self.intake
class Meta:
db_table = "Intake"
verbose_name_plural = "Intakes"
ordering = ['intake']
i am using the following views to check if some thing exist in data base and add to database
from ragendja.template import render_to_response
from django.http import HttpResponse, Http404
from google.appengine.ext import db
from timekeeper.forms import *
from timekeeper.models import *
def checkintake(request, key):
intake = Intake.all().filter('intake=',key).count()
if intake<1:
return HttpResponse('ok')
else:
return HttpResponse('Exist in database')
def addintake(request,key):
if Intake.all().filter('intake=',key).count()>1:
return HttpResponse('Item already Exist in Database')
else:
data = Intake(intake=cleaned_data[key])
data.put()
return HttpResponse('Ok')
i can add to database with no problem (when i do a Intake.all().count() it increases) but when i check if the key exist in the database by filtering i am getting a count of zero any one have any idea why i am not able to filter by keys ?
You need to insert a space between the field name and the operator in your filter arguments - eg, use .filter('intake =') instead of .filter('intake='). With an equality filter, you can also leave it out entirely, as in .filter('intake'). Without the space, the equals sign is taken to be part of the field name.