Flask-SqlAlchemy RuntimeError: No application found. Either work inside a view function or push an application context - flask-sqlalchemy

I try to implement Flask-SqlAlchemy in a Quart application it MVC structure. So I want every Model Have its file and functions.
This is my files:
app.py
__package__ = 'nini'
from .setups import create_app
from .db import db
if __name__ == '__main__':
app = create_app()
db.init_app(app)
app.run(host='127.0.0.1', debug=True)
db.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
tests.py Model file
from nini.db import db
import datetime
class Test(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.VARCHAR)
created = db.Column(db.DATETIME, default=datetime.datetime.now())
pass
def __init__(self, text):
self.text = text
def __repr__(self):
return '<Test %r>' % self.id, self.created
#classmethod
async def create_new(cls, **kw):
obj = cls(**kw)
db.session.add(obj)
db.session.commit()
pass
test.py
import logging
from quart import jsonify, app, current_app
from quart_openapi import PintBlueprint
from datetime import datetime
from nini.models import tests
from nini.db import db
results = PintBlueprint('test', __name__)
#results.route('/test/test')
async def get_tests():
logging.error("HELLO")
t = await tests.Test.create_new(text="TT")
logging.error("Done")
return jsonify(t), 200
When I run /test/test path it works fine until `db.session.add(obj)1 line.
Then I get this error:
RuntimeError: No application found. Either work inside a view function
or push an application context. See
http://flask-sqlalchemy.pocoo.org/contexts/.
INFO:quart.serving:127.0.0.1:50537 GET /test/test 1.1 500 - 21836
I tried adding this code at app.py:
db.init_app(app) ### This line was already in my code
with app.app_context():
db.create_all()
Also like this:
db.init_app(app) ### This line was already in my code
app.app_context().push():
db.create_all()
I also tried running create_new at tasts.py with context.
Tried implementing the create_all and test model inside db.py
Basically after db.init_app(app) whenever I use db I get the error.
Would love for some help.

Related

How to do filtering according to log in user?

I want to get hobbys according to log in user but I am always getting
TypeError at /backend/api/hobbys/
init() takes 1 positional argument but 2 were given
this is my views.py
class ListCreateHobbyView(GenericAPIView):
queryset = Hobby.objects.all()
serializer_class = HobbySerializer
# Filtering by logged in user
def get(self, request, *args, **kwargs):
queryset = Hobby.objects.filter(user=request.user)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
What can be wrong?
Please do not override the entire .get(…) method. This means that (nearly) all boilerplate code that Django has written is no longer applied. You filter in the .get_queryset(…) method [drf-doc]:
from rest_framework.generics import ListAPIView
class ListCreateHobbyView(ListAPIView):
queryset = Hobby.objects.all()
serializer_class = HobbySerializer
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
user=self.request.user
)
You can also make a custom filter backend: this is a reusable component that you then can use in other views. This thus looks like:
from rest_framework import filters
class UserFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(user=request.user)
then you use this as filter backend:
from rest_framework.generics import ListAPIView
class ListCreateHobbyView(ListAPIView):
queryset = Hobby.objects.all()
serializer_class = HobbySerializer
filter_backends = [UserFilterBackend]
The advantage of this approach is that if you later construct extra views that need the same filtering, you can simply "plug" these in.

Cannot pass object instance from conftest.py to a test class

I am trying to create a session-scope fixture that creates and pass instance of the driver class for every single test class in my suite. I was expecting that the following code would work:
import pytest
from pages.home.home_page import HomeAdmin
from base.webdriver_factory import WebDriverFactory
#pytest.fixture(scope='session', autouse=True)
def startup(request):
print("SESSION SET UP")
wdf = WebDriverFactory("firefox")
driver = wdf.get_web_driver_instance() # returns driver instance
return driver
I was expecting to have acces to the driver instance from my test code:
from pages.home.home_page import HomeAdmin
import unittest
import pytest
#pytest.mark.usefixtures("startup")
class HomeAdminTest(unittest.TestCase):
#pytest.fixture(autouse=True)
def setup(self, startup):
print("TEST")
self.ha = HomeAdmin(self.driver)
def test_login(self):
print("test run")
Buy this results in an error:
#pytest.fixture(autouse=True)
def setup(self, startup):
print("TEST")
> self.ha = HomeAdmin(self.driver)
E AttributeError: 'HomeAdminTest' object has no attribute 'driver'
testcases\home\home_test.py:11: AttributeError
What I'm trying to achieve in general:
Open browser only once for all tests (all classes and modules) and then run various other classes to manipulate with the same driver instance. (I know that this is not best practice for testing, but this is special case and I am going rather automate some processes than making real tests).
Thank you in advance,
Wojciech
Finally I figured out how to solve problem by myself. It turned out that I was refering to the instance of webdriver in the test class in wrong way. Properly working class looks as following:
from pages.home.home_page import HomeAdmin
import unittest
import pytest
#pytest.mark.usefixtures("startup")
class HomeAdminTest(unittest.TestCase):
#pytest.fixture(autouse=True)
def setup(self,startup):
print("TEST")
self.ha = HomeAdmin(startup)
def test_login(self):
print("test run")
Now I have access to my driver and page class created with it.

DB not changing when testing Flask-Security with peewe and PyTest

I just got into testing my flask application with pytest, and it mostly works as expected. Unfortunately the test uses the live DB instead a mock one. I'm quite sure this has to do with the fact, that flask-security is using peewee's database_wrapper instead of an "straightforward" database.
Here's some code. This is from the test:
#pytest.fixture
def client():
db_fd, belavoco_server.app.config['DATABASE'] = { 'name': 'userLogin_TEST.db',
'engine': 'peewee.SqliteDatabase' } }
belavoco_server.app.config['TESTING'] = True
client = belavoco_server.app.test_client()
#this seems not to help at all
with belavoco_server.app.app_context():
belavoco_server.users.user_managment.db_wrapper.init_app(belavoco_server.app)
yield client
os.close(db_fd)
os.unlink(belavoco_server.app.config['DATABASE'])
This is some code from my bv_user_model.py
app.config['DATABASE'] = {
'name': 'userLogin.db',
'engine': 'peewee.SqliteDatabase',
}
app.config['SECURITY_URL_PREFIX'] = "/users"
# Create a database instance that will manage the connection and
# execute queries
db_wrapper = FlaskDB(app)
class Role(db_wrapper.Model, RoleMixin):
name = CharField(unique=True, default="standard")
description = TextField(null=True)
def __repr__(self):
return self.name
When preforming the test, Flask is using the userLogin.db instead of userLogin_TEST.db. I suppose this is because of the db_wrapper in bv_user_model.py - but I did not find a way to change this behaviour. Any help would be greatly appreciated!
The root of the issue seems to be this in bv_user_model:
app.config['DATABASE'] = {
'name': 'userLogin.db',
'engine': 'peewee.SqliteDatabase',
}
Since you are using FlaskDB with the app that has the production credentials, it seems like the db_wrapper will "remember" that and not be overridden by your tests.
The most straightforward answer would be to not use your app to create the FlaskDB instance directly
db = FlaskDB()
And then later on initialize it on your app
from models import db
def create_app():
app = ...
app.config["DATABASE"] = ...
db.init_app(app)
...
return app
Which would let you have a separate function like this which you can use for testing.
def create_test_app():
app = ...
app.config["DATABASE"] = ...test credentials...
db.init_app(app)
...
return app
and when you create your models, use the FlaskDB instance just the same as you were already.
db = FlaskDB()
class Role(db.Model, RoleMixin):
...

Custom scrapy xml+rss exporter to S3

I'm trying to create a custom xml feed, that will contain the spider scraped items, as well as some other high level information, stored in the spider definition. The output should be stored on S3.
The desired output looks like the following:
<xml>
<title>my title defined in the spider</title>
<description>The description from the spider</description>
<items>
<item>...</item>
</items>
</xml>
In order to do so, I defined a custom exporter, which is able to export the desired output file locally.
spider.py:
class DmozSpider(scrapy.Spider):
name = 'dmoz'
allowed_domains = ['dmoz.org']
start_urls = ['http://www.dmoz.org/Computers/']
title = 'The DMOZ super feed'
def parse(self, response):
...
yield item
exporters.py:
from scrapy.conf import settings
class CustomItemExporter(XmlItemExporter):
def __init__(self, *args, **kwargs):
self.title = kwargs.pop('title', 'no title found')
self.link = settings.get('FEED_URI', 'localhost')
super(CustomItemExporter, self).__init__(*args, **kwargs)
def start_exporting(self):
...
self._export_xml_field('title', self.title)
...
settings.py:
FEED_URI = 's3://bucket-name/%(name)s.xml'
FEED_EXPORTERS = {
'custom': 'my.exporters.CustomItemExporter',
}
I'm able to run the whole thing and get the output on s3 by running the following command:
scrapy crawl dmoz -t custom
or, if I want to export a json locally instead: scrapy crawl -o dmoz.json dmoz
But at this point, I'm unable to retrieve the spider title to put it in the output file.
I tried implementing a custom pipeline, which outputs data locally (following numerous examples):
pipelines.py:
class CustomExportPipeline(object):
def __init__(self):
self.files = {}
#classmethod
def from_crawler(cls, crawler):
pipeline = cls()
crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
return pipeline
def spider_opened(self, spider):
file = open('%s_feed.xml' % spider.name, 'w+b')
self.files[spider] = file
self.exporter = CustomItemExporter(
file,
title = spider.title),
)
self.exporter.start_exporting()
The problem is, the file is stored locally, and this short circuits the FeedExporter logic defined in feedexport.py, that handles all the different storages.
No info from the FeedExporter is available in the pipeline, and I would like to reuse all that logic without duplicating code. Am I missing something? Thanks for any help.
Here's my solution:
get rid of the pipeline.
Override scrapy's FeedExporter
myproject/feedexport.py:
from scrapy.extensions.feedexport import FeedExporter as _FeedExporter
from scrapy.extensions.feedexport import SpiderSlot
class FeedExporter(_FeedExporter):
def open_spider(self, spider):
uri = self.urifmt % self._get_uri_params(spider)
storage = self._get_storage(uri)
file = storage.open(spider)
extra = {
# my extra settings
}
exporter = self._get_exporter(file, fields_to_export=self.export_fields, extra=extra)
exporter.start_exporting()
self.slot = SpiderSlot(file, exporter, storage, uri)
All I wanted to do was basically to pass those extra settings to the exporter, but the way it's built, there is no choice but to override.
To support other scrapy export formats simultaneously, I would have to consider overriding the dont_fail settings to True in some scrapy exporters to prevent them from failing
Replace scrapy's feed exporter by the new one
myproject/feedexport.py:
EXTENSIONS = {
'scrapy.extensions.feedexport.FeedExporter': None,
'myproject.feedexport.FeedExporter': 0,
}
... or the 2 feed exporters would run at the same time

Subclass `pathlib.Path` fails

I would like to enhance the class pathlib.Path but the simple example above dose not work.
from pathlib import Path
class PPath(Path):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
test = PPath("dir", "test.txt")
Here is the error message I have.
Traceback (most recent call last):
File "/Users/projetmbc/test.py", line 14, in <module>
test = PPath("dir", "test.txt")
File "/anaconda/lib/python3.4/pathlib.py", line 907, in __new__
self = cls._from_parts(args, init=False)
File "/anaconda/lib/python3.4/pathlib.py", line 589, in _from_parts
drv, root, parts = self._parse_args(args)
File "/anaconda/lib/python3.4/pathlib.py", line 582, in _parse_args
return cls._flavour.parse_parts(parts)
AttributeError: type object 'PPath' has no attribute '_flavour'
What I am doing wrong ?
You can subclass the concrete implementation, so this works:
class Path(type(pathlib.Path())):
Here's what I did with this:
import pathlib
class Path(type(pathlib.Path())):
def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None):
if encoding is None and 'b' not in mode:
encoding = 'utf-8'
return super().open(mode, buffering, encoding, errors, newline)
Path('/tmp/a.txt').write_text("я")
Here is the definition of the Path class. It does something rather clever. Rather than directly returning an instance of Path from its __new__(), it returns an instance of a subclass, but only if it's been invoked directly as Path() (and not as a subclass).
Otherwise, it expects to have been invoked via either WindowsPath() or PosixPath(), which both provide a _flavour class attribute via multiple inheritance. You must also provide this attribute when subclassing. You'll probably need to instantiate and/or subclass the _Flavour class to do this. This is not a supported part of the API, so your code might break in a future version of Python.
TL;DR: This idea is fraught with peril, and I fear that my answers to your questions will be interpreted as approval rather than reluctant assistance.
You may be able to simplify your life depending on why you want to extend Path (or PosixPath, or WindowsPath). In my case, I wanted to implement a File class that had all the methods of Path, and a few others. However, I didn't actually care if isinstance(File(), Path).
Delegation works beautifully:
class File:
def __init__(self, path):
self.path = pathlib.Path(path)
...
def __getattr__(self, attr):
return getattr(self.path, attr)
def foobar(self):
...
Now, if file = File('/a/b/c'), I can use the entire Path interface on file, and also do file.foobar().
Combining some of the previous answers you could also just write:
class MyPath(pathlib.Path):
_flavour = type(pathlib.Path())._flavour
I have been struggling with this too.
Here is what i did, studying from the pathlib module.
Seems to me that is the cleaner way to do it, but if the pathlib module changes its implementation, it probably won't hold.
from pathlib import Path
import os
import pathlib
class PPath(Path):
_flavour = pathlib._windows_flavour if os.name == 'nt' else pathlib._posix_flavour
def __new__(cls, *args):
return super(PPath, cls).__new__(cls, *args)
def __init__(self, *args):
super().__init__() #Path.__init__ does not take any arg (all is done in new)
self._some_instance_ppath_value = self.exists() #Path method
def some_ppath_method(self, *args):
pass
test = PPath("dir", "test.txt")
Note
I have opened a bug track here after a little discussion on the Python dev. list.
A temporary solution
Sorry for this double answer but here is a way to achieve what I want. Thanks to Kevin that points me to the source of pathlib and the fact we have here constructors.
import pathlib
import os
def _extramethod(cls, n):
print("=== "*n)
class PathPlus(pathlib.Path):
def __new__(cls, *args):
if cls is PathPlus:
cls = pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath
setattr(cls, "extramethod", _extramethod)
return cls._from_parts(args)
test = PathPlus("C:", "Users", "projetmbc", "onefile.ext")
print("File ?", test.is_file())
print("Dir ?", test.is_dir())
print("New name:", test.with_name("new.name"))
print("Drive ?", test.drive)
test.extramethod(4)
This prints the following lines.
File ? False
Dir ? False
New name: C:/Users/projetmbc/new.name
Drive ?
=== === === ===
In order to inherit from pathlib.Path, you need to specify which OS, or "flavour" you're representing. All you need to do is specify that you are using either Windows or Unix (seems to be Unix based on your traceback) by inheriting from pathlib.PosixPath or pathlib.WindowsPath.
import pathlib
class PPath(pathlib.PosixPath):
pass
test = PPath("dir", "test.txt")
print(test)
Which outputs:
dir\test.txt
Using type(pathlib.Path()) as proposed in this answer does the exact same thing as directly inheriting from pathlib.PosixPath or pathlib.WindowsPath since instantiating pathlib.Path "creates either a PosixPath or a WindowsPath" (pathlib documentation).
If you know your application will not be cross-platform, it is simpler to directly inherit from the flavor Path that represents your OS.
Here is a simple way to do things regarding to the observation made by Kevin.
class PPath():
def __init__(self, *args, **kwargs):
self.path = Path(*args, **kwargs)
Then I will need to use a trick so as to automatically bind all the Path's methods to my PPpath class. I think that will be funny to do.
It's work too.
from pathlib import Path
class SystemConfigPath(type(Path())):
def __new__(cls, **kwargs):
path = cls._std_etc()
return super().__new__(cls, path, **kwargs)
#staticmethod
def _std_etc():
return '/etc'
name = SystemConfigPath()
name = name / 'apt'
print(name)
Printed:
/etc/apt
#staticmethod can be replaced by #classmethod