Import Django class from view to another file - python

Good afternoon everybody,
I'm fighting with my custom class defined in my Django view in order to use it inside another python file which let me to manage my Menu.
This class is pretty simple, but she uses a custom middleware too:
# main.views.py file
class MyClass:
def my_main_function(self, request):
my_queryset = myModel.objects.filter(application=request.cur_app, display=True).order_by('order')
return my_queryset
def toto(self):
list_test = self.my_main_function()
return list_test
request.cur_app corresponds to my middleware defined in middleware.py file.
I created toto() because I would like to use my_main_function inside another file called menus.py which doesn't handle request.
In this file, I have:
from .views.main import MyClass
for element in MyClass.toto():
... do something ...
But this is the issue encountered:
toto() missing 1 required positional argument: 'self'
I'm conscient there is a possibility of huge issues in my code, because I'm learning programmation at the same time. Be indulgent.

Related

Can I define classes in Django settings, and how can I override such settings in tests?

We are using Django for Speedy Net and Speedy Match (currently Django 1.11.17, we can't upgrade to a newer version of Django because of one of our requirements, django-modeltranslation). I want to define some of our settings as classes. For example:
class UserSettings(object):
MIN_USERNAME_LENGTH = 6
MAX_USERNAME_LENGTH = 40
MIN_SLUG_LENGTH = 6
MAX_SLUG_LENGTH = 200
# Users can register from age 0 to 180, but can't be kept on the site after age 250.
MIN_AGE_ALLOWED_IN_MODEL = 0 # In years.
MAX_AGE_ALLOWED_IN_MODEL = 250 # In years.
MIN_AGE_ALLOWED_IN_FORMS = 0 # In years.
MAX_AGE_ALLOWED_IN_FORMS = 180 # In years.
MIN_PASSWORD_LENGTH = 8
MAX_PASSWORD_LENGTH = 120
PASSWORD_VALIDATORS = [
{
'NAME': 'speedy.core.accounts.validators.PasswordMinLengthValidator',
},
{
'NAME': 'speedy.core.accounts.validators.PasswordMaxLengthValidator',
},
]
(which is defined in https://github.com/speedy-net/speedy-net/blob/staging/speedy/net/settings/global_settings.py). And then in the models, I tried to use:
from django.conf import settings as django_settings
class User(ValidateUserPasswordMixin, PermissionsMixin, Entity, AbstractBaseUser):
settings = django_settings.UserSettings
(and then use attributes of settings, such as settings.MIN_USERNAME_LENGTH, in the class).
But it throws an exception
AttributeError: 'Settings' object has no attribute 'UserSettings'
(but it doesn't throw an exception if I use there a constant which is not a class).
This is the first problem. In the meantime, I defined instead:
from speedy.net.settings import global_settings as speedy_net_global_settings
class User(ValidateUserPasswordMixin, PermissionsMixin, Entity, AbstractBaseUser):
settings = speedy_net_global_settings.UserSettings
The second problem, is how do I override such settings in tests? For example, I use the following code:
from speedy.core.settings import tests as tests_settings
#override_settings(MAX_NUMBER_OF_FRIENDS_ALLOWED=tests_settings.OVERRIDE_MAX_NUMBER_OF_FRIENDS_ALLOWED)
in https://github.com/speedy-net/speedy-net/blob/staging/speedy/core/friends/tests/test_views.py. But if MAX_NUMBER_OF_FRIENDS_ALLOWED would be defined in the class UserSettings, how do I override it?
Django doesn't expect you to deviate much from its low-level design choices and it's usually a struggle to work around things that Django doesn't explicitly allow you to customize.
Django's settings object explicitly skips over any objects in your settings module with non-uppercase names. If you rename your class to USER_SETTINGS, it will work. If you really want to keep your object's original name a horrible solution would be to trick Django:
class UserSettings:
...
class AlwaysUppercaseStr(str):
def isupper(self):
return True
globals()[AlwaysUppercaseStr('UserSettings')] = globals().pop('UserSettings')
I have no idea if this is portable across Python implementations but it works with CPython's dir().
override_settings has no support for what you're trying to do so you will probably need to rewrite that class to allow the global settings object to be configurable.
Thanks to #Blender for the tip:
Django's settings object explicitly skips over any objects in your
settings module with non-uppercase names. If you rename your class to
USER_SETTINGS, it will work.
I was not aware that all the settings have to be uppercase. So I renamed class UserSettings to class USER_SETTINGS (although PyCharm doesn't like it), but I checked and it's also possible to add this code at the end of the file:
USER_SETTINGS = UserSettings
Without renaming the class.
As for my second question - how do I override such settings in tests? I added a file called utils.py:
def get_django_settings_class_with_override_settings(django_settings_class, **override_settings):
class django_settings_class_with_override_settings(django_settings_class):
pass
for setting, value in override_settings.items():
setattr(django_settings_class_with_override_settings, setting, value)
return django_settings_class_with_override_settings
(You can see it on https://github.com/speedy-net/speedy-net/blob/staging/speedy/core/base/test/utils.py)
And then in the tests:
from django.conf import settings as django_settings
from django.test import override_settings
from speedy.core.settings import tests as tests_settings
from speedy.core.base.test.utils import get_django_settings_class_with_override_settings
#override_settings(USER_SETTINGS=get_django_settings_class_with_override_settings(django_settings_class=django_settings.USER_SETTINGS, MAX_NUMBER_OF_FRIENDS_ALLOWED=tests_settings.OVERRIDE_USER_SETTINGS.MAX_NUMBER_OF_FRIENDS_ALLOWED))
def test_user_can_send_friend_request_if_not_maximum(self):
self.assertEqual(first=django_settings.USER_SETTINGS.MAX_NUMBER_OF_FRIENDS_ALLOWED, second=4)
I checked and I have to define another class (in this case, class django_settings_class_with_override_settings because if I change the class django_settings_class directly it also affects other tests which didn't use #override_settings.

Where should I put these functions? Inside models.py or inside views.py?

I am developing a web app using Django.
There are few functions like, get_token, get_url etc to fetch data from the database and also to modify the database.
Where should I put those functions? Inside views.py or models.py?
Since those functions are interacting with the database directly I am thinking of putting those in the models.py file to use those functions as an abstraction.
I would like to know the best practice.
models.py is meant to be used to define Classes (each takes up a
table in the database).
views.py contains any functions that are used
to return django rendered pages. It definitely contains any view functions and other functions that are called within the view.
Since your functions get_token() & get_url(), are processing data (and not creating, modifying or deleting tables), i'd suggest you to put them in the views.py file.
A sample way you might approach this is as below:
models.py:
class UrlTokenPair(models.Model):
url = models.models.TextField(blank=True)
token = models.TextField(blank=True)
views.py
from app_name.models import UrlTokenPair
def get_token(input_url):
if UrlTokenPair.objects.filter(url=input_url):
return UrlTokenPair.objects.get(url=input_url).token
else:
new_entry = UrlTokenPair.objects.create(url=input_url)
new_entry.token = yourAlgoForToken()
new_entry.save()
return new_entry.token
def get_url(input_token):
if UrlTokenPair.objects.filter(token=input_token):
return UrlTokenPair.objects.get(url=input_token).url
else:
new_entry = UrlTokenPair.objects.create(token=input_token)
new_entry.url = yourAlgoForUrl()
new_entry.save()
return new_entry.url
You can then call the above functions get_url and get_token in any view function in the views.py file. You can also call them in another .py file by just referring to them as:
from app_name.views import get_url, get_token

Cannot Overriding Class HttpRequest Method dispatch in web module?

Iam trying to override a method inside web=>http.py file in openerp project .
I have write this code :
#in my_http.py
from openerp.addons import web
class my_httprequest(web.http.HttpRequest):
def dispatch(self, method):
#some new code here ...
but will not overriding the original ?
where is the problem , why cannot override ?
Did you put the 'my_http.py' statement in 'openerp.py' file ?

Django Admin import_export module used for custom IMPORT

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')

Repeating class decorators in a Web.py / MIMERender Application

I'm new to Python and obviously I am finding it awesome.
Specifically, I'm building a small data API using Web.py and MIMERender.
I've organized it thusly: each table in the DB has a corresponding module with three classes.
# Define URL endpoints
urls = (
# Index
'/', 'index',
# Disclosures
'/disclosure/listAll', 'Disclosures',
'/disclosure/page/(.+)', 'DisclosurePage',
'/disclosure/(.+)', 'Disclosure',
)
Each of those classes are defined in a separate file. For example, the three disclosure classes are defined in disclosure.py: (note: this is very psuedo as the definitions are too involved and would distract from the main question)
import web
# Define response formats
render_xml = lambda **args: dict2xml(args).display()
render_json = lambda **args: json.dumps(args)
class Disclosure:
#mimerender(
default = 'json',
xml = render_xml,
json = render_json,
)
def GET(self, primaryKey):
... ( retrieval and formatting ) ...
return { 'response' : someResponse }
At the top of each module, I define the lambda functions as per MIMERender and then in each of the class definitions I include the neccesary class decorator.
Problem is, I seem to be repeating myself. In each module, as in the example above, there are three classes to define and I have to put the very same class decorator in each one. Also, the same lamba function is included at the beginning of each module.
I could cut out a 17 or more LOC per module if I were more familiar with how Python deals with this sort of thing.
Is there any way I can make A) the module as a whole inherit the lambda function definitions, and B) each of the class methods inherit the class decorator?
Thanks in advance!
I think the easiest way to clean this up is to put your render functions and decorator into a separate module, e.g. foo.py, like this:
from mimerender import FlaskMimeRender
def render_json(**view):
""" do whatever """
def render_xml(**view):
""" do whatever """
def render_html(**view):
""" do whatever """
mimerender = FlaskMimeRender()(
default = "html",
html = render_html,
xml = render_xml,
json = render_json,
)
Now your class file looks like this:
from foo import mimerender
class Disclosure:
#mimerender
def GET(self, primaryKey):
""" do stuff """
The only trick here is that foo.py needs to be in your path or else you need to do a relative import, but those are entirely separate questions.

Categories