Get orderable fields - Django Rest - reactjs

I have a ReactJS table that uses fetch API to get the data from a Django server using Rest. I've defined the viewset to use OrderingFilter and specified the ordering_fields as so:
class RecipientViewSet(BaseViewSetClass):
permission_classes = (IsAuthenticated,)
serializer_class = serializers.RecipientsSerializer
filter_backends = (filters.OrderingFilter,)
ordering_fields = (
'id',
'externalID',
'firstname',
'lastname',
)
It works great, but I want to know if there's a method of getting from the ordering_fields from the server with fetch, so that I can add a sort icon only for the table headers that can be used to set the data's ordering.
I should add that in the Django REST Framework GUI I can click "Filters" button and view all possible filters for this viewset, and that's exactly what I want to get with ReactJS.
Thanks for any help!

I don't think there is a way to get the ordering fields out of the box via an API call, but you can always add an endpoint to return them:
from rest_framework import viewsets
from rest_framework.decorators import list_route
class MyViewSet(viewsets.GenericViewSet):
ordering_fields = ('a', 'b')
#list_route
def get_ordering_fields(self, request):
return self.ordering_fields
This way calling /your_endpoint/get_ordering_fields/ will return the list of ordering fields defined in the class. You can apply any formatting before returning them if you wish.
Also keep in mind that this behavior can be implemented in a base or mixin class so it can be reused easily later.
See the docs about list and detail routes.

Related

How can i filter a Product by price in DRF

I have a model class Products in which I simple specify product-URL, product-title and product-price as attributes. and make Serializer.py file use ModelSerializer and pass data to Reactjs Now I can't understand that how can i filter products by there price so if I fetch API in react all High price are sorted first or low after according to user need or our option...thanks
There are multiple ways you can do this:
1. Using DRF Filter Backends:
DRF Filtering Documentation - Ordering
Based on the official DRF documentation, you need to specify your filter backends in your settings or your view like so:
from rest_framework import filters
class ProductListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = ProductSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ['price']
You're going to be able to filter your list with a request parameter:
http://example.com/api/products?ordering=price
Or reverse ordering by:
http://example.com/api/products?ordering=-price
2. hardcoding the sorting in your queryset
class ProductListView(generics.ListAPIView):
queryset = User.objects.all().order_by('-score')
serializer_class = ProductSerializer
Basically, you don't want to hardcode your ordering unless you are always sure you want the results to be ordered that way. Otherwise, filter backends are the way to go.

Django Dynamic model register in admin

I'm using django 1.11 and I tried to to create django dynamic models by referring this link https://code.djangoproject.com/wiki/DynamicModels , by executing each and every step it runs without any issue, but How can I see this created table in django admin panel?
action.py
from django.db import models
from django.contrib import admin
def create_model(name, fields=None, app_label='', module='', options=None, admin_opts=None):
"""
Create specified model
"""
class Meta:
# Using type('Meta', ...) gives a dictproxy error during model creation
pass
if app_label:
# app_label must be set using the Meta inner class
setattr(Meta, 'app_label', app_label)
# Update Meta with any options that were provided
if options is not None:
for key, value in options.iteritems():
setattr(Meta, key, value)
# Set up a dictionary to simulate declarations within a class
attrs = {'__module__': module, 'Meta': Meta}
# Add in any fields that were provided
if fields:
attrs.update(fields)
# Create the class, which automatically triggers ModelBase processing
model = type(name, (models.Model,), attrs)
# Create an Admin class if admin options were provided
if admin_opts is not None:
print admin_opts
class Admin(admin.ModelAdmin):
pass
for key, value in admin_opts:
setattr(Admin, key, value)
admin.site.register(model, Admin)
return model
In Console:
from action import create_model
from django.db import models
fields = {
'first_name': models.CharField(max_length=255),
'last_name': models.CharField(max_length=255),
'__str__': lambda self: '%s %s' (self.first_name, self.last_name),
}
options = {
'ordering': ['last_name', 'first_name'],
'verbose_name': 'valued customer',
}
admin_opts = {}
model = create_model('Person', fields,
options=options,
admin_opts=admin_opts,
app_label='form',
module='project.app.model',
)
I can see no. of fields by
len(model._meta.fields)
But I have no idea of, how to register the created model in admin, and what parameter will come inside admin_opts = {} , how can i do makemigrations and migrate,how can I access this model in views.py, from where i will import this model .Can you guys please help me for this , it will be very useful for me and Thanks in advance.
with connection.schema_editor() as editor:
editor.create_model(Model)
This is from github source code , try it instead of sql_model_create and I try to success in my project,and it's true..
I have worked hard for a long time because I don't find django-dynamic-model in "django 1.10".
I think you forgot to execute this function.
def install(model):
from django.core.management import sql, color
from django.db import connection
# Standard syncdb expects models to be in reliable locations,
# so dynamic models need to bypass django.core.management.syncdb.
# On the plus side, this allows individual models to be installed
# without installing the entire project structure.
# On the other hand, this means that things like relationships and
# indexes will have to be handled manually.
# This installs only the basic table definition.
# disable terminal colors in the sql statements
style = color.no_style()
cursor = connection.cursor()
statements, pending = sql.sql_model_create(model, style)
for sql in statements:
cursor.execute(sql)

Tastypie filter by minimum value

I have a Django-tastypie resource that represents a banner and has a field called impression that I increment whenever the banner appears on the site.
class BannerResource(ModelResource):
owner = fields.ForeignKey('advertisment.api.AdvertiserResource', 'owner', full=True)
class Meta:
queryset = Banner.objects.all()
resource_name = 'banner'
authorization = Authorization()
I would like to get the banner that has the minimum impression, in the official documentation there is nothing like
filtering = {'impressions': ('min',)}
I'm using BackboneJS in the front end and I could get all the banners with Backbone collection and do the filtering with JavaScript but I'm looking for a quicker way to do it.
Any ideas?
Thanks
If you'd like to retrieve banners with number of impressions greater than X you need to things. For one you need to define possible filtering operations on your resource like so (given your model has impressions field):
class BannerResource(ModelResource):
owner = fields.ForeignKey('advertisment.api.AdvertiserResource', 'owner', full=True)
class Meta:
queryset = Banner.objects.all()
resource_name = 'banner'
authorization = Authorization()
filtering = { 'impressions' : ALL }
for available options take a look at Tastypie's documentation on filtering.
Then if you made the following request:
GET http://<your_host>/v1/banners?impressions__gte=X
you should get what you need.

How to show all fields of model in admin page?

here is the models page
In this picture, only the title shows up on here, I used:
def __unicode__(self):
return self.title;
here is the each individual objects
How do I show all these fields?
How do I show all the fields in each Model page?
If you want to include all fields without typing all fieldnames, you can use
list_display = BookAdmin._meta.get_all_field_names()
The drawback is, the fields are in sorted order.
Edit:
This method has been deprecated in Django 1.10 See Migrating from old API for reference. Following should work instead for Django >= 1.9 for most cases -
list_display = [field.name for field in Book._meta.get_fields()]
By default, the admin layout only shows what is returned from the object's unicode function. To display something else you need to create a custom admin form in app_dir/admin.py.
See here: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display
You need to add an admin form, and setting the list_display field.
In your specific example (admin.py):
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'price')
admin.site.register(Book, BookAdmin)
If you want to include all but the ManyToManyField field names, and have them in the same order as in the models.py file, you can use:
list_display = [field.name for field in Book._meta.fields if field.name != "id"]
As you can see, I also excluded the id.
If you find yourself doing this a lot, you could create a subclass of ModelAdmin:
class CustomModelAdmin(admin.ModelAdmin):
def __init__(self, model, admin_site):
self.list_display = [field.name for field in model._meta.fields if field.name != "id"]
super(CustomModelAdmin, self).__init__(model, admin_site)
and then just inherit from that:
class BookAdmin(CustomModelAdmin):
pass
or you can do it as a mixin:
class CustomModelAdminMixin(object):
def __init__(self, model, admin_site):
self.list_display = [field.name for field in model._meta.fields if field.name != "id"]
super(CustomModelAdminMixin, self).__init__(model, admin_site)
class TradeAdmin(CustomModelAdminMixin, admin.ModelAdmin):
pass
The mixin is useful if you want to inherit from something other than admin.ModelAdmin.
I found OBu's answer here to be very useful for me. He mentions:
The drawback is, the fields are in sorted order.
A small adjustment to his method solves this problem as well:
list_display = [f.name for f in Book._meta.fields]
Worked for me.
The problem with most of these answers is that they will break if your model contains ManyToManyField or ForeignKey fields.
For the truly lazy, you can do this in your admin.py:
from django.contrib import admin
from my_app.models import Model1, Model2, Model3
#admin.register(Model1, Model2, Model3)
class UniversalAdmin(admin.ModelAdmin):
def get_list_display(self, request):
return [field.name for field in self.model._meta.concrete_fields]
Many of the answers are broken by Django 1.10. For version 1.10 or above, this should be
list_display = [f.name for f in Book._meta.get_fields()]
Docs
Here is my approach, will work with any model class:
MySpecialAdmin = lambda model: type('SubClass'+model.__name__, (admin.ModelAdmin,), {
'list_display': [x.name for x in model._meta.fields],
'list_select_related': [x.name for x in model._meta.fields if isinstance(x, (ManyToOneRel, ForeignKey, OneToOneField,))]
})
This will do two things:
Add all fields to model admin
Makes sure that there is only a single database call for each related object (instead of one per instance)
Then to register you model:
admin.site.register(MyModel, MySpecialAdmin(MyModel))
Note: if you are using a different default model admin, replace 'admin.ModelAdmin' with your admin base class
Show all fields:
list_display = [field.attname for field in BookModel._meta.fields]
Every solution found here raises an error like this
The value of 'list_display[n]' must not be a ManyToManyField.
If the model contains a Many to Many field.
A possible solution that worked for me is:
list_display = [field.name for field in MyModel._meta.get_fields() if not x.many_to_many]
I like this answer and thought I'd post the complete admin.py code (in this case, I wanted all the User model fields to appear in admin)
from django.contrib import admin
from django.contrib.auth.models import User
from django.db.models import ManyToOneRel, ForeignKey, OneToOneField
MySpecialAdmin = lambda model: type('SubClass'+model.__name__, (admin.ModelAdmin,), {
'list_display': [x.name for x in model._meta.fields],
'list_select_related': [x.name for x in model._meta.fields if isinstance(x, (ManyToOneRel, ForeignKey, OneToOneField,))]
})
admin.site.unregister(User)
admin.site.register(User, MySpecialAdmin(User))
I'm using Django 3.1.4 and here is my solution.
I have a model Qualification
model.py
from django.db import models
TRUE_FALSE_CHOICES = (
(1, 'Yes'),
(0, 'No')
)
class Qualification(models.Model):
qual_key = models.CharField(unique=True, max_length=20)
qual_desc = models.CharField(max_length=255)
is_active = models.IntegerField(choices=TRUE_FALSE_CHOICES)
created_at = models.DateTimeField()
created_by = models.CharField(max_length=255)
updated_at = models.DateTimeField()
updated_by = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'qualification'
admin.py
from django.contrib import admin
from models import Qualification
#admin.register(Qualification)
class QualificationAdmin(admin.ModelAdmin):
list_display = [field.name for field in Qualification._meta.fields if field.name not in ('id', 'qual_key', 'qual_desc')]
list_display.insert(0, '__str__')
here i am showing all fields in list_display excluding 'id', 'qual_key', 'qual_desc' and inserting '__str__' at the beginning.
This answer is helpful when you have large number of modal fields, though i suggest write all fields one by one for better functionality.
list_display = [field.name for field in Book._meta.get_fields()]
This should work even with python 3.9
happy coding
I needed to show all fields for multiple models, I did using the following style:
#admin.register(
AnalyticsData,
TechnologyData,
TradingData,
)
class CommonAdmin(admin.ModelAdmin):
search_fields = ()
def get_list_display(self, request):
return [
field.name
for field in self.model._meta.concrete_fields
if field.name != "id" and not field.many_to_many
]
But you can also do this by creating a mixin, if your models have different fields.

django model/modelForm - How to get dynamic choices in choiceField?

i'm experimenting with django and the builtin admin interface.
I basically want to have a field that is a drop down in the admin UI. The drop down choices should be all the directories available in a specified directory.
If i define a field like this:
test_folder_list = models.FilePathField(path=/some/file/path)
it shows me all the files in the directory, but not the directories.
Does anyone know how i can display the folders?
also i tried doing
test_folder_list = models.charField(max_length=100, choices=SOME_LIST)
where SOME_LIST is a list i populate using some custom code to read the folders in a directory. This works but it doesn't refresh. i.e. the choice list is limited to a snapshot of whatever was there when running the app for the first time.
thanks in advance.
update:
after some thinking and research i discovered what i want may be to either
1. create my own widget that is based on forms.ChoiceField
or
2. pass my list of folders to the choice list when it is rendered to the client
for 1. i tried a custom widget.
my model looks like
class Test1(models.Model):
test_folder_ddl = models.CharField(max_length=100)
then this is my custom widget:
class FolderListDropDown(forms.Select):
def __init__(self, attrs=None, target_path):
target_folder = '/some/file/path'
dir_contents = os.listdir(target_folder)
directories = []
for item in dir_contents:
if os.path.isdir(''.join((target_folder,item,))):
directories.append((item, item),)
folder_list = tuple(directories)
super(FolderListDropDown, self).__init__(attrs=attrs, choices=folder_list)
then i did this in my modelForm
class test1Form(ModelForm):
test_folder_ddl = forms.CharField(widget=FolderListDropDown())
and it didn't seem to work.What i mean by that is django didn't want to use my widget and instead rendered the default textinput you get when you use a CharField.
for 2. I tried this in my ModelForm
class test1Form(ModelForm):
test_folder_ddl = forms.CharField(widget=FolderListDropDown())
test_folder_ddl.choices = {some list}
I also tried
class test1Form(ModelForm):
test_folder_ddl = forms.ChoiceField(choices={some list})
and it would still render the default char field widget.
Anyone know what i'm doing wrong?
Yay solved. after beating my head all day and going through all sorts of examples by people i got this to work.
basically i had the right idea with #2. The steps are
- Create a ModelForm of our model
- override the default form field user for a models.CharField. i.e. we want to explcitly say use a choiceField.
- Then we have to override how the form is instantiated so that we call the thing we want to use to generate our dynamic list of choices
- then in our ModelAdmin make sure we explicitly tell the admin to use our ModelForm
class Test1(models.Model):
test_folder_ddl = models.CharField(max_length=100)
class Test1Form(ModelForm):
test_folder_ddl = forms.choiceField()
def __init__(self, *args, **kwargs):
super(Test1Form, self).__init__(*args, **kwargs)
self.fields['test_folder_ddl'].choices = utility.get_folder_list()
class Test1Admin(admin.ModelAdmin):
form = Test1Form
I use a generator:
see git://gist.github.com/1118279.git
import pysvn
class SVNChoices(DynamicChoice):
"""
Generate a choice from somes files in a svn repo
""""
SVNPATH = 'http://xxxxx.com/svn/project/trunk/choices/'
def generate(self):
def get_login( realm, username, may_save ):
return True, 'XXX', 'xxxxx', True
client = pysvn.Client()
client.callback_get_login = get_login
return [os.path.basename(sql[0].repos_path) for sql in client.list(self.SVNPATH)[1:]]

Resources