Django create custome admin view with non database fields - python

I would like to create a custome admin view with fields with data that I fill manually, meaning that there's no database table behind.
In my admin.py I have this so far
class TestAdmin(admin.ModelAdmin):
pass
admin.site.register(Test, TestAdmin)
and in the models.py
class Test(models.Model):
test = models.BooleanField(
default=True
)
But with this I receive an error
django.db.utils.ProgrammingError: relation "..._test" does not exist
Django is looking up the table in the database but in my model I need only fields which data I fill manually

It seems that you don't really get what a Model and ModelAdmin are. A Model class is the representation of a database table, and a ModelAdmin is a component that provides administration features for this table and it's content. IOW, when you write that "there's no database table behind", you're missing the point somehow: you cannot have a ModelAdmin without a Model, and you cannot have a Model without a database table.
To make a long story short: you'll either have to
1/ use a model or
2/ write the whole view(s) and templates by yourself and plug them into the admin.
For the second solution, see this question (and it's answers), or just google for "django admin views without models" (there are a couple 3rd part apps that are supposed to help plugin custom views in the admin).
Now the first solution might - or not, depending on your exact needs - be as good as the second one, depending on who populates your s3bucket and how. Without a model, you'll have to query the bucket for a file listing each and every time someone accesses your custom admin view, which might get quite costly. If all the uploads to the s3 bucket are under your control, you can use a model to keep a local file listing "cache" and you'll only need to hit s3 when effectively downloading a file. This also gives you the opportunity to store metadata about those files - which can be used for searching / filtering - and eventually to link your s3 files to other models if needed. And of course this means you get your admin for free ;)
Actually even if you don't have control on those uploads, you may still want to use a model for the above reasons and have a background task (cron job or equivalent) that updatees your table from the s3 bucket contents (or - if s3 provides web hooks - have an API endpoint called by s3 on uploads that updates your table).

The way that django's ORM works is that all class members of a model class that is an instance of model.Field will map to a column in the database.
It is possible to have model properties that does not map to a database table. You could use a #property instead, for example.
class Test(models.Model):
#property
def test(self):
return get_aws_bucket_value(self.id)
#test.setter
def test(self, value):
set_aws_bucket_value(self.id, value)
You have to implement the aws getter/setter functions yourself.
You can use properties as read-only fields in the django admin class. Be careful about using it in the list view if your getter logic needs to fetch data from a remote server syncronously, since that would be a significant performance bottleneck.
If you want to be able to set values for a setter from the django admin, you would have to write a custom ModelForm for TestAdmin.
It is possible to wrangle Django into doing what you want here. But Django's models and admin are based around the ORM, which means using a relational database as persistant storage. If you don't want that, you can find yourself fighting against and overriding the framework more often than benefiting from Django's "batteries included". This project might be better solved with a less monolithic and less opinionated framework such as Flask instead of Django.

Related

Migrating to Django rest framework

Currently, I am using node as backend and looking forward to shift my project to django-rest-framework. In node I used firebase-auth and MySQL and have a table called FIREBASE_USERS with email and firebase_uid fields. I was thinking of building my own custom auth for drf but can't figure out on using my FIREBASE_USERS table instead of django Users model. I read that django provides settings. AUTH_USER_MODEL to set your own custom model but the documentation uses AbstractUser class to create that and I don't want to mess with my tables since the tables are used in other projects too. Any suggestions would be appreciated.
Your custom user model needs to contain the properties as described in (1). AbstractBaseUser is a shortcut to achieve this. Override the Meta.db_table to match you table name. If this is somehow not sufficient, consider creating your own authentication backend as described in (2) and create a django user with a foreign key to your firebase user. But in order to let django function properly your user model needs to have the methods as described in (1).
Maybe (3) solves your problem?
(1) https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#django.contrib.auth.models.CustomUser
(2) https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#writing-an-authentication-backend
(3) https://github.com/fcornelius/django-firebase-auth

executing django model query?

In django model query, i want to know the sequential execution of it. Consider a query Blog.objects.get(name='palm').
In this where the Blog is defined, is that same as class blog in models.py?
What is objects i can't find anything related to this in source files of django. If Blog is a class, then what is the type of objects?
I want a development side concept. Can any one please explain how django makes these possible?
Every non-abstract Django model class has an attribute objects attached to it (unless you of course explicitly remove it).
object is a Manager. It is an object that has a lot of methods to construct queries that are then send to the database to fetch/store data.
So you first access the objects manager of the Blog class, next you call .get(name='palm') on it. This means that Django will translate this into a query. This depends on the database system you use. For instance if it is MySQL it will look like:
SELECT name, some, other columns
FROM app_blog
WHERE name = 'palm'
The database will respond with zero, one or more rows, and Django will, in case no or more than one row is found, raise a DoesNotExists or MultipleObjectsReturned error. Otherwise it will load the data into a Blog object (by deserializing the columns into Python objects).

Extend User Model or Custom Pipeline in Social-App-Django

I am implementing social-app-django (not the deprecated one; the one that relies on Python-social-auth) with django 1.11 (not using Mongo). My application will need to store and manipulate a lot of data on users other than that which is fetched from their social media accounts at login.
I don't need to fetch or collect any extra data when the user authenticates, but various actions they perform on my site will need to be saved to their user model. I am wondering which of the following approaches is preferred (I've searched extensively online, but can't find a specific explanation of why to use one vs the other):
Create my own user model in my app's models.py (call it MyUser) that doesn't extend anything special, and then add a function in the authentication pipeline that associates the social-app-django user with a corresponding instance of MyUser. Leave AUTH_USER_MODEL and SOCIAL_AUTH_USER_MODEL unchanged.
or...
Create my own user model in my app's models.py, and in the project's settings.py set AUTH_USER_MODEL and SOCIAL_AUTH_USER_MODEL to point to MyUser. Leave the pipeline unchanged. In this case, I was wondering whether someone could clarify what MyUser and its manager should extend, and what I need to import in modules.py (I am confused because a lot of stack overflow posts are referring to deprecated versions of this module and I keep getting errors). Also, in this case should I be setting both AUTH_USER_MODEL and SOCIAL_AUTH_USER_MODEL, or just one of them?
Do these two methods essentially achieve the same thing? Is one more reliable/preferred for some reason? Or, should I be doing both? Thanks very much for any assistance.
Another detail: I would like to be able to access the User database not only from the app I am currently building, but also from other apps (within the same Django project) that I will build in the future. Does this affect anything?
Since I see this has a decent number of views I will post the solution I eventually came to.
Both django and social-app-django (or any other social auth module) make use of the default User model for authentication. While it's possible to edit this model to add custom parameters, I don't recommend it. It's not good abstraction or modularization. If you make a mistake when configuring the model, you won't just break a specific feature on your site, but you might also break the authentication itself.
The only circumstances I can think of under which you'd want to edit the default user model itself is if you need to make changes that affect the authentication flow itself (for example, adding your own custom authentication provider).
It's much easier and safer to create a new model called UserProfile, with a required one-to-one relationship to a User object. Now, you can treat the User object as the authentication part, and the UserProfile object as the content/storage part. You won't have to mess with the User model very often, and the UserProfile model doesn't matter for authentication purposes. Note that in this configuration you should NOT need to change the AUTH_USER_MODEL or SOCIAL_AUTH_USER_MODEL fields in the settings.py file.
If you take this approach, you will need to add a custom step in the authentication pipeline in which you create a new UserProfile object and associate it with the User who is currently logging in.

Django Admin Non Staff Access Data Filtering

I'm writing an application in Django (which I'm very new to) where the admin area will be exposed to 'customers' of the application, not just staff/superusers, because of the nature of the application and the way Django automatically generates forms in the admin area with such little code..
As such I need to robust and manageable way to maintain authentication and separating data, so only data created by a user is seen by that user.
At the moment I'm just using the default admin package and changing permissions for 'client users' to filter what data they can see (I only want them to see data they've created) using code like the below:
class MyModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(MyModelAdmin, self).get_queryset(request)
return qs.filter(user=request.user)
def save_model(self, request, obj, form, change):
# field not editable in admin area so handle it here...
obj.user = request.user
obj.save()
However as the application scales, I can see ensuring this type of data filtering becoming difficult to manage, for example if there are chains of foreign keys on certain tables(A->B B->C C->D), and to filter the table at the end of the chain I need to do various JOINs to get the rows which relate to the current user.
A couple of solutions I'm pondering are creating a separate admin app per user, but this feels like overkill and even more unmanageable.
Or just adding the user column to every Model where data filtering by user is required to make it easier to filter.
Any thoughts on the best approach to take?
First off, from experience, you're better off offering editing and creating functionality to your users in an actual django app, using Views. Generic views make this very easy. Once you let your users into the admin, they will get used to it and it's hard to get them to leave.
Additionally you should use contrib.auth.Group together with django-guardian to keep track of object-level permissions instead of implementing it yourself. It pays off in the long run.
If you want to make this experience on your own however, you have more than one sensible choice:
owner on root objects in the ForeignKey pyramid
owner on every model
To realize the first option, you should implement two methods on every model down the ForeignKey chain:
def get_parent(self):
"""Should return the object that should be queried for ownership information"""
pass
def owned_by(self, user):
"""Should return a boolean indicating whether `user` owns the object, by querying `self.get_parent().owned_by(user)`"""
pass
However, as you stated, this incurrs many JOINS if your schema is sufficiently complex.
I would advise you to store the information about the owner in every model, everything else is a maintanence nightmare in my experience.
Instead of adding the field manually to every model manually, you should use inheritance. However django provides bad built-in support for inheritance with relations: An abstract base model cannot define a models.ForeignKey, so you're stuck with table based inheritance.
Table based inheritance brings another problem with itself: Consider these models:
from django.db import models
from app.settings import AUTH_USER_MODEL
class Base(models.Model):
owner = models.ForeignKey(AUTH_USER_MODEL)
class ChildA(Base):
name = models.CharField(max_length=5)
class ChildB(Base):
location = models.CharField(max_length=5)
It is easy to find the owner of a given instance of ChildA or ChildB:
>>> obj = ChildA.objects.create(owner=Peter, name="alex")
>>> obj.owner
Peter
However it is non trivial to find all objects owned by a particular user:
>>> Base.objects.filter(owner=Peter)
<Base-object at 0xffffff>
The default manager returns a Base object, and doesn't contain information about whether it is a ChildA or ChildB instance, which can be troublesome.
To circumvent this, I recommend a polymorphic approach with django-polymorphic or django-model-utils, which is more lightweight. They both provide means to retrieve the child classes for a given Base model in the queries.
See my answer here for more information on polymorphism in django.
These also incur JOINs, but at least the complexity is manageable.

Django: Filter request results to only contain data related to the requesting user

I'm a Django beginner (though I do have experience in web development using Sails.js + Angular) so bear with me.
I have an existing application that uses REST API in communicating between Sails.js backend and AngularJS frontend. Now, we've found the backend to be unsuited for our purposes, and we're going to swap to using Django in near-future. Sails.js automatically creates the REST methods for the controllers while Django doesn't, so I suppose I'm going to use something like Django Rest Framework to create the API.
So yeah, I've found corresponding features for most things. The on thing I haven't found yet is a replacement for a Sails.js feature called "policies". They are functions that can be executed on queries to certain controller actions, and can be defined as model-specific, model-controller action-specific, and request type specific. For example, you can have an "authAccess" policy that checks that the user of a request is authenticated, and the policy gets executed before the actual requested controller method gets executed. You can also use these to modify request objects before passing them to the controller. Now to my actual problem:
Let's say I have a User model that has a many-to-one relation with another model, let's call it Book, meaning a user can own many books, but a book can only have one owner. Goody good. Now, we have a logged-in user that is making a query to find all of his books. He makes a GET request to /book. I want to ensure that the returned Book objects are filtered so that ONLY HIS BOOKS are returned.
So basically in Sails I was able to write a policy that altered the request parameters to be like {user: loggedInUser} so the resulting Book results were automatically filtered. This was handy, since I was able to use the same policy to filter other models, too, like DVD or Friend or whatnot. My question is - what would be the best way to implement the same functionality in Django?
Have a look at the documentation:
http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-the-current-user
Most likely you are better off overwriting the get_queryset method in a model viewset. And you can make this a generic approach by creating a base class for your views, something like:
from rest_framework import generics, viewsets, mixins, generics
class OwnerModelViewSet(viewsets.ModelViewSet):
def get_queryset(self):
"""
This view should return a list of all the records
for the currently authenticated user.
"""
return self.model.objects.filter(user=self.request.user)
All your model viewset classes can inherit from that class. It would require the foreign key field to be always named "user" though. If that is not the case here is a slightly hacky way how you could find a foreign key field to the User table. Use with care.
from django.db.models.fields.related import ForeignKey
from accounts.models import User
def _get_related_user(self, obj):
'''
Search for FK to user model and return field name or False if no FK.
This can lead to wrong results when the model has more than one user FK.
'''
for f in self.model._meta.fields:
if isinstance(f, ForeignKey) and f.rel.to == User:
return f.name
return False

Categories

Resources