GraphQL: Can Queries & Mutations be split into separate classes? - django

In my Graphene-Django project, I have this structure:
Project level:
schema.py
App level:
schema.py
queries.py
mutations.py
This works well but the queries file has grown quite large. Is there a way to split class Query into multiple classes and/or multiple files?
Robert

From the perspective of the project schema file, you can use inheritance in your Query class to pull in each of your separate Query classes. You can use the same technique to split your Mutation. For example:
from django.conf import settings
import graphene
from graphene_django.debug import DjangoDebug
from app1.schema import App1Query, App1Mutation
from app2.schema import App2Query, App2Mutation
class Query(App1Query, App2Query, graphene.ObjectType):
if settings.DEBUG:
# Debug output - see
# http://docs.graphene-python.org/projects/django/en/latest/debug/
debug = graphene.Field(DjangoDebug, name='__debug')
class Mutation(App1Mutation, App2Mutation, graphene.ObjectType):
pass
schema = graphene.Schema(query=Query, mutation=Mutation)
(Note that I'm also dynamically adding the debug class if DEBUG is True -- this has nothing to do with your question, but it's handy.)
You should able to use the same inheritance technique to further split your query if you need to, for example breaking apart App1Query.

Related

How to import the same flask limiter in a structured flask app

I'm trying to organize my Flask app, as it's getting quite big in length at close to 1000 lines
I am trying to separate the REST API from my main app, by using the approach shown here: https://flask-restx.readthedocs.io/en/latest/scaling.html#multiple-apis-with-reusable-namespaces
What remains in my main.py is something like
from apiv1 import blueprint as api1
REST_API = Flask(__name__)
REST_API.wsgi_app = ProxyFix(REST_API.wsgi_app, x_for=1)
REST_API.register_blueprint(api1)
However in my app, I am using the flask limiter
# Very basic DOS prevention
try:
limiter = Limiter(
REST_API,
key_func=get_remote_address,
storage_uri="redis://localhost:6379/1",
# storage_options={"connect_timeout": 30},
strategy="fixed-window", # or "moving-window"
default_limits=["90 per minute"]
)
# Allow local workatation run
except:
limiter = Limiter(
REST_API,
key_func=get_remote_address,
default_limits=["90 per minute"]
)
This is likewise placed in a decorator to my various API functions
decorators = [limiter.limit("30/minute")]
def post(self, server_id = ''):
# [..]
Now that I am splitting my REST api from the same file that declaring my endpoints, I don't know how to pass its object. The REST_API var exists only in my main.py
How should I handle passing the limiter variable, or any other global objects for that matter?
I worked for a few hours yesterday but I finally understood the pythonic way to do this sort of thing.
I just couldn't wrap my head around how imports function so I was struggling with questions like "how do I pass the variable during import" etc.
Finally it clicked for me that I need to follow a "pull" method with my imports, instead of trying to push variables into them. I.e. I setup the center location in my package's __init__ which will import my logger module, and then my other modules will import THAT logger variable from there.
So in my app's __init__, I have
from .limiter import limiter
And in the app/apis/v1.py I have
from .. import limiter
And this seems to finally work. I don't know if this is the expected way, meaning to play with relative module paths, so if there;s a more elegant way, please let me know

How do I unit test django urls?

I have achieved 100% test coverage in my application everywhere except my urls.py. Do you have any recommendations for how I could write meaningful unit tests for my URLs?
FWIW This question has arisen as I am experimenting with Test-Driven Development and want failing tests before I write code to fix them.
One way would be to reverse URL names and validate
Example
urlpatterns = [
url(r'^archive/(\d{4})/$', archive, name="archive"),
url(r'^archive-summary/(\d{4})/$', archive, name="archive-summary"),
]
Now, in the test
from django.urls import reverse
url = reverse('archive', args=[1988])
assertEqual(url, '/archive/1988/')
url = reverse('archive-summary', args=[1988])
assertEqual(url, '/archive-summary/1988/')
You are probably testing the views anyways.
Now, to test that the URL connect to the right view, you could use resolve
from django.urls import resolve
resolver = resolve('/summary/')
assertEqual(resolver.view_name, 'summary')
Now in the variable resolver (ResolverMatch class instance), you have the following options
'app_name',
'app_names',
'args',
'func',
'kwargs',
'namespace',
'namespaces',
'url_name',
'view_name'
Just complementing the answer from #karthikr (on the basis of his)
-> you may assert that the view in charge of resolve is the one you'd expect using resolver.func.cls
example
from unittest import TestCase
from django.urls import resolve
from foo.api.v1.views import FooView
class TestUrls(TestCase):
def test_resolution_for_foo(self):
resolver = resolve('/api/v1/foo')
self.assertEqual(resolver.func.cls, FooView)
Sorry for beeing a miner but I found this place as a first under key words "django url testing".
I fairly agree with my predecessors but I am also sure there is a better way to test your URLs. The main reason why we should not use "resolver = resolve('url/path')" is that the paths are of some kind fluent when the view's names are more fixed.
Well in simple words, this is better:
class TestUrls(TestCase):
def test_foo_url_resolves(self):
url = reverse('bar:foo')
self.assertEqual(resolve(url).func.view_class, FooBarView)
'bar:foo' - bar is a namespace and foo is a view name
than this
class TestUrls(TestCase):
def test_resolution_for_foo(self):
resolver = resolve('/api/v1/foo')
self.assertEqual(resolver.func.cls, FooView)

Django test not loading fixture data

I have written tests for a Django project that i am working on, but one particular fixture fails to load.
The fixture is generated using dumpdata and i havent fiddled with it at all.
I can load the data using manage.py on that fixture without errors. I have verified that the data actually loaded using shell and querying the data.
This is driving me nuts, any help would be much appreciated.
Here is my test file (irrelevant portions removed):
class ViewsFromUrls(TestCase):
fixtures = [
'centers/fixtures/test_data.json',
'intranet/fixtures/test_data.json',
'training/fixtures/test_data.json', #The one that fails to load
]
def setUp(self):
self.c = Client()
self.c.login(username='USER', password='PASS')
...
def test_ViewBatch(self):
b = Batch.objects.all()[0].ticket_number
response = self.c.get(reverse('training.views.view_batch', kwargs={'id':b}))
self.assertTrue(response.status_code, 200)
...
Import the TestCase from django.test:
from django.test import TestCase
class test_something(TestCase):
fixtures = ['one.json', 'two.json']
...
Not: import unittest
Not: import django.utils.unittest
But: import django.test
That's a day of frustration right there.
Stop complaining - it's in the docs :-/
I Am not sure if this fixes your problem, but on this site:
https://code.djangoproject.com/wiki/Fixtures
I found an interesting remark:
you see that Django searches for appnames/fixtures and
settings.FIXTURE_DIRS and loads the first match. So if you use names
like testdata.json for your fixtures you must make sure that no other
active application uses a fixture with the same name. If not, you can
never be sure what fixtures you actually load. Therefore it is
suggested that you prefix your fixtures with the application names,
e.g. myapp/fixtures/myapp_testdata.json .
Applying this (renaming the fixtures with appname as prefix in the filename), solved my problem (I had the same issue as described here)
Check if the fixture is really in the right place. From the docs:
Django will search in three locations
for fixtures:
In the fixtures directory of every installed application
In any directory named in the FIXTURE_DIRS setting
In the literal path named by the fixture
One thing to note, when creating the FIXTURE_DIRS constant in your settings file, be sure to leave out the leading '/' if you have a general fixtures directory off of the root of your project.
Ex:
'/actual/path/to/my/app/fixtures/'
Now, in the settings.py file:
Will NOT work:
FIXTURE_DIRS = '/fixtures/'
Will work:
FIXTURE_DIRS = 'fixtures/'
It's possible this depends on how your other routes are configured, but it was a gotcha that had me scratching my head for a little while. Hope this is useful. Cheers.
A simple mistake I made was adding a custom setUpClass() and forgetting to include super().setUpClass() with it (which of course, is where Django's logic for loading fixtures lives)

Dynamically generating Hudson custom workspace path

I'm trying to get a Hudson job to get built in a custom workspace path that is automatically generated using yyyyMMdd-HHmm. I can get the $BUILD_ID variable expanded as mentioned in bug 3997, and that seems to work fine. However, the workspace path is incorrect as it is of the format yyyy-MM-dd_HH-mm-ss. I've tried using the ZenTimestamp plugin v2.0.1, which changes the $BUILD_ID, but this only seems to take effect after the workspace is created.
Is there a method of defining a custom workspace in the manner that I want it?
You can use a groovy script to achieve that.
import hudson.model.*;
import hudson.util.*;
import java.util.*;
import java.text.*;
import java.io.*;
//Part 1 : Recover build parameter
AbstractBuild currentBuild = (AbstractBuild) Thread.currentThread().executable;
def envVars= currentBuild.properties.get("envVars");
def branchName = envVars["BRANCH_NAME"];
//Part 2 : Define new workspace Path
def newWorkspace = "C:\\Build\\"+branchName;
//Part 3 : Change current build workspace
def newWorspaceFilePath = new FilePath(new File(newWorkspace));
currentBuild.setWorkspace(newWorspaceFilePath);

is it possible to create a custom admin view without a model behind it

I have an object which I want to use under admin instead of a model which inherits models.Model. If I make it inherit models.Model, this object will create a table in the database which i don't want. I only want this object to stay in memory.
One solution I have come with help from the nice people at stack overflow is I create admin views, register these custom views via a modelAdmin ( admin.site.register() ) under admin.py and use this model-like object as dynamic data storage (in memory).
Since this model like object doesn't inherit from models.Model, admin.site.register() (under admin.py) doesnt accept it and shows a 'type' object is not iterable" error when I try to access it in the browser.
hmmm. Thanks for your help everyone. The solution I have come up ( with your help ofcourse :) is as follows:
I have two custom templates:
my_model_list.html
my_model_detail.html
Under views.py:
class MyModel(object):
# ... Access other models
# ... process / normalise data
# ... store data
#staff_member_required
def my_model_list_view(request) #show list of all objects
#. . . create objects of MyModel . . .
#. . . call their processing methods . . .
#. . . store in context variable . . .
r = render_to_response('admin/myapp/my_model_list.html', context, RequestContext(request))
return HttpResponse(r)
#staff_member_required
def my_model_detail_view(request, row_id) # Shows one row (all values in the object) in detail
#. . . create object of MyModel . . .
#. . . call it's methods . . .
#. . . store in context variable . . .
r = render_to_response('admin/myapp/my_model_detail.html', context, RequestContext(request))
return HttpResponse(r)
Under the main django urls.py:
urlpatterns = patterns(
'',
(r'^admin/myapp/mymodel/$', my_model_list_view),
(r'^admin/myapp/mymodel/(\d+)/$', my_model_detail_view),
( r'^admin/', include( admin.site.urls ) )
)
You can add your views directly to the AdminSite object, rather than to any particular ModelAdmin subclass which you then register.
The default AdminSite is accessed via django.contrib.admin.site, which is what you call register and autodiscover on. Instead of using this, you could create your own subclass and add your own views to it, and then register your models against that rather than the default one.
The most straightforward answer is "no". As the Django Book says, the admin is for "Trusted users editing structured content," in this case the structured content being models arranged in hierarchies and configured through settings.py. More importantly, if your object doesn't completely duck-type to a models.Model complete with expected relationships, the admin will probably toss exceptions all over the place.
However, as the mantra goes, "It's just python." You can override any of the pages in admin. Just create your own templates in your project, and have them come first in the template search. Also, by inheriting admin/base.html, you maintain the look & feel of the administration project.
Write your administrative view and templates for this object, just like any others, but making sure to wrap the views in the is_staff decorator to ensure that the views are protected from access by unauthorized users. Put those in the application, perhaps in admin/views.py, with templates/admin/object_list.html and object_form.html.
Once you have appropriate administrative tools for these non-database objects, you can then provide access to them through the administration index page: You want to override admin/index.html, and provide additional project-specific items to the page as needed.
I have done exactly this to provide administrative access to third-party APIs that store our data, such as the ConstantContact email service, and it works pretty well.