How do I get the filepath for a class in Python? - python

Given a class C in Python, how can I determine which file the class was defined in? I need something that can work from either the class C, or from an instance off C.
The reason I am doing this, is because I am generally a fan off putting files that belong together in the same folder. I want to create a class that uses a Django template to render itself as HTML. The base implementation should infer the filename for the template based on the filename that the class is defined in.
Say I put a class LocationArtifact in the file "base/artifacts.py", then I want the default behaviour to be that the template name is "base/LocationArtifact.html".

You can use the inspect module, like this:
import inspect
inspect.getfile(C.__class__)

try:
import sys, os
os.path.abspath(sys.modules[LocationArtifact.__module__].__file__)

This is the wrong approach for Django and really forcing things.
The typical Django app pattern is:
/project
/appname
models.py
views.py
/templates
index.html
etc.

Related

Django ImportError: Module does not define attribute/class

I am trying to add a custom/overridden AdminSite because I need a different template for the Admin. I did everything as the Docs said:
https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#overriding-the-default-admin-site
project/admin.py
from django.contrib.admin import AdminSite
class NewAdminSite(AdminSite):
index_template = 'admin/index.html'
site_header = 'Administration'
admin_site = NewAdminSite(name='newadmin')
project/apps.py
class NewAdminConfig(AdminConfig):
default_site = 'project.admin.NewAdminSite'
I don't have any other apps, project is my root directory
ofc I added admin_site.urls instead of admin.site.urls to urls.py, created a custom AdminConfig in apps.py, and added that new AdminConfig to installed Apps instead of django.contrib.admin.
The Problem is that I now receive this:
AdminSiteClass = import_string(apps.get_app_config('admin').default_site)
File "C:\Users\User.virtualenvs\ssccms-fGQRLLK4\lib\site-packages\django\utils\module_loading.py", line 24, in import_string
) from err
ImportError: Module "project.admin" does not define a "NewAdminSite" attribute/class
my folder structure:
manage.py
project
admin
settings
urls
apps
static
templates
migrations
workdir
sqlite
media
node_modules
locale
pipfile.lock
pipfile
wsgi
hope this is enough for folder structure, it is the django-shop cookiecutter project example structure
EDIT:
yes, it was definitely a circular import error. My workaround now is simply using the given admin.site by writing:
admin.site.index_template = 'admin/newadminindextemplate.html'
it works! Which is actually all that I wanted. But the Documentation says the following
https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#root-and-login-templates
If you wish to change the index, login or logout templates, you are better off creating your own AdminSite instance.
I tried that:
https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#customizing-the-adminsite-class
instead of the overriding part I pasted in the first link in the question but now there is
django.urls.exceptions.NoReverseMatch: Reverse for 'cascade_texteditor_config' not found. 'cascade_texteditor_config' is not a valid view function or pattern name.
now my new question is:
is it necessary to create your own instance of the index template ? the Documentation says no explanation so are there any problems that I might run into at a later time by doing admin.site.index_template?
I've just hit a similar error. It's a bit confusing, in my case it was due to a circular import.
In my case the following helped:
convert imports from top of the file containing NewAdminSite class to local imports (inside of the function where they're needed
move the instantiation of admin_site variable to a separate file
Neither of those problems is actually visible in the code you've pasted, but may give you an idea what to look for.

Django-Viewflow, customize archive view

I am trying to customize the default /workflow/archive/ view from Viewflow.
(as in http://demo.viewflow.io/workflow/archive/)
I need to remove some of the columns and add some additional ones specific to my task.
I can't seem to find an easy way. I have been digging and have found that I can try to override the class viewflow.frontend.views.AllArchiveListView and adding the mapping
url('^test/', AllArchiveListView.as_view())
but I get no data when doing that (seems like the flow_classes internal parameter is empty when it gets called like that)
I have also tried to create the viewflow/site_archive.html template but seems overkill.
Any ideas that may point me in the right direction would be highly appreciated!
To customize a common frontend list view, you need to replace viewflow.frontend with you own frontend application. To simplify development, you can inherit from viewflow frontend base classes
# apps.py
from django.apps import AppConfig
from viewflow.frontend.apps import ViewflowFrontendConfig
class FrontendConfig(ViewflowFrontendConfig):
viewset = 'frontend.viewset.FrontendViewSet'
def register(self, flow_class, viewset_class=None):
super().register(flow_class, viewset_class=viewset_class)
# views.py
from viewflow.frontend.views import AllTaskListView as BaseAllTaskListView
class AllTaskListView(BaseAllTaskListView):
list_display = [
'task_hash', 'description'
]
# viewset.py
from viewflow.frontend.viewset import FrontendViewSet as BaseFrontendViewSet
class FrontendViewSet(BaseFrontendViewSet):
inbox_view_class = views.AllTaskListView

Custom Add and Edit Template Dextertiy

'I have a package called foo.package. And I also have a content item called mydocument.py. As a standard structure, inside foo/package I have browser, content, profiles, etc. What I want to achieve is to create a customized add template for my content item which is mydocument.
I have read the article from this link http://docs.plone.org/external/plone.app.dexterity/docs/advanced/custom-add-and-edit-forms.html
What I have done is that I created a file inside browser directory and named it as mydocumentaddform.py. Also, inside my browser directory I have templates directory.
The code inside mydocumentaddform.py:
from five import grok
from plone.directives import dexterity, form
from Products.CMFCore.utils import getToolByName
grok.templatedir('templates')
class mydocumentaddform(dexterity.AddForm):
grok.name('foo.package.mydocument')
form.wrap(False)
Inside browser/templates directory I created the template mydocumentaddform.pt. However, my customized mydocumentaddform template was not rendered as I add the mydocument content, since inside the template I added js script to detect whether the customized add template override the default add template. As I view my instance, there is a UserWarning that there is an unassociated template configuration which points to my add template: ~... /foo/package/browser/templates/mydocumentaddform.pt
Is there anyone who have encountered the same problem? Or is there any other way to do this? Thanks.
*Yes I forgot to include the quotes here on grok.name.
Update:
I found a temporary solution, inside the class ViewPageTemplateFile. Here's the updated code:
from five import grok
from plone.directives import dexterity, form
from Products.CMFCore.utils import getToolByName
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
grok.templatedir('templates')
class mydocumentaddform(dexterity.AddForm):
grok.name('foo.package.mydocument')
template = ViewPageTemplateFile('templates/mydocumentaddform.pt')
form.wrap(False)
The customized add template was read, however, still my instance always says that the template that I used is still unassociated. As I also observed, when I replace the base class into dexterity.DisplayForm, the instance error is gone. With this temporary solution, I am not sure of any possible effect in the future due to this persistent error.
I see some issues in your code: you're missing the quotes on the name:
grok.name('foo.package.mydocument')
you need to define the context:
grok.context(IMyDocument)
did you declare an add-on layer? then you need:
grok.layer(IMyLayer)
you can find a working example in collective.nitf.

Why would I put code in __init__.py files?

I am looking for what type of code would I put in __init__.py files and what are the best practices related to this. Or, is it a bad practice in general ?
Any reference to known documents that explain this is also very much appreciated.
Libraries and frameworks usually use initialization code in __init__.py files to neatly hide internal structure and provide a uniform interface to the user.
Let's take the example of Django forms module. Various functions and classes in forms module are defined in different files based on their classification.
forms/
__init__.py
extras/
...
fields.py
forms.py
widgets.py
...
Now if you were to create a form, you would have to know in which file each function is defined and your code to create a contact form will have to look something like this (which is incovenient and ugly).
class CommentForm(forms.forms.Form):
name = forms.fields.CharField()
url = forms.fields.URLField()
comment = forms.fields.CharField(widget=forms.widgets.Textarea)
Instead, in Django you can just refer to various widgets, forms, fields etc. directly from the forms namespace.
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)
How is this possible? To make this possible, Django adds the following statement to forms/__init__.py file which import all the widgets, forms, fields etc. into the forms namespace.
from widgets import *
from fields import *
from forms import *
from models import *
As you can see, this simplifies your life when creating the forms because now you don't have to worry about in where each function/class is defined and just use all of these directly from forms namespace. This is just one example but you can see examples like these in other frameworks and libraries.
One of the best practices in that area is to import all needed classes from your library (look at mongoengine, for example). So, a user of your library can do this:
from coollibrary import OneClass, SecondClass
instead of
from coollibrary.package import OneClass
from coollibrary.anotherpackage import SecondClass
Also, good practice is include in __init__.py version constant
For convenience: The other users will not need to know your functions' exactly location.
your_package/
__init__.py
file1.py/
file2.py/
...
fileN.py
# in __init__.py
from file1 import *
from file2 import *
...
from fileN import *
# in file1.py
def add():
pass
then others can call add() by
from your_package import add
without knowing file1, like
from your_package.file1 import add
Put something for initializing. For example, the logging(this should put in the top level):
import logging.config
logging.config.dictConfig(Your_logging_config)

Django: tests.py adds project name to class

In my views.py I have a piece of code that checks if a model inherits from an other model.
def inherits_from_animal(some_animal):
return Animal in inspect.getmro(some_animal)
Now in my views.py this works but in my tests it's failing because the name of the project gets added to the class name.
So if I have a project named zoo and an app named animals it looks like:
views.py:
<class 'animals.models.Animal'>
tests.py:
<class 'zoo.animals.models.Animal'>
Could anybody explain why this is happening and how I could fix it?
Update:
Seems to only be happening to an abstract model.
The problem is manage.py. If you put django apps inside other packages you should use their fully qualified module name for imports and as a corrolary in INSTALLED_APPS.
It still works if you omit the parent package name, because manage.py temporarily hacks sys.path to import the module that contains the settings module. Then nose does its own import magic and is unable to reproduce the manage.py hack.
I'd recommend to use django-admin.py instead of manage.py as it does not do any import tricks.
There is no concept of a project in django. Just treat everything as plain python packages and control your PYTHONPATH.
the inspect module is doing probably something hinky.
Python has the builtin function issubclass() for just this occasion!
def inherits_from_animal(some_animal):
return issubclass(some_animal, Animal)
although i dont see why you couldnt just use issubclass wherever you are using inherits_from_animal now ;)

Categories

Resources