I'm trying to create an API endpoint on my Django project to retrieve data from my frontend.
I'm using two DBs on my django project, the first one is a SQLite DB, the second one is a MongoDB database, the data i need to retrieve is on MongoDB.
Here is my model:
class tst(models.Model):
_id = models.CharField(max_length=100)
ticker = models.FloatField()
def save(self): # ALL the signature
super(Trade, self).save(using='dbtwo')
Here is my view:
class tstList(generics.ListCreateAPIView):
queryset = tst.objects.all()
serializer_class = tstSerializer
And the url:
path('tst/', views.tstList.as_view()),
Everything is alright here but when i try to open the API from my browser, i keep getting the following error:
OperationalError at /tst/
no such table: main_tst
I think this happens because it tries to look for the table tst on the first SQLite database, instead of looking for it on the MongoDB one. Is there any way to solve this? I thought that adding using='dbtwo' would do it, but it's not the right solution.
Every advice is appreciated!
You need to define the database that you are using in the queryset for your API view
class tstList(generics.ListCreateAPIView):
queryset = tst.objects.using('dbtwo').all()
serializer_class = tstSerializer
Even better than this, if the model will only ever use the other database, you can set up a router so that you do not have to set "using" every time
class MyRouter:
def db_for_read(model, **hints):
if model == tst:
return 'dbtwo'
def db_for_write(model, **hints):
if model == tst:
return 'dbtwo'
# In your settings
DATABASE_ROUTERS = ['path.to.MyRouter']
Related
I have a program that lets users upload data files and lookup tables (both which are ID'd to a specific company) and map them together. One page lets users choose which company they want to map data for by looking at which companies have both data files and lookup tables, which I use a queryset/model manager for. The problem is if I load a new data file and hierarchy the queryset doesn't pick them up until the server restarts. The queryset returns all the companies that have a data file and hierarchies at the time the server starts, but not anything that's added afterwards. I think this must be because the queryset is defined at startup, but I'm not sure. Is there a way to work around this?
forms.py
class CompanySelectionForm(forms.Form):
companies = RawData.objects.get_companyNames(source="inRDandH")
companiesTuple = makeTuple(companies)
print(companiesTuple)
company = forms.ChoiceField(widget=forms.Select(attrs={'class': 'form-select'}), choices=companiesTuple)
managers.py
class RawDataManager(models.Manager):
def get_queryset(self):
return RawDataQuerySet(self.model, using=self._db)
def get_companyNames(self, source):
return self.get_queryset().get_companyNames(source)
class RawDataQuerySet(models.QuerySet):
def get_companyNames(self, source):
if (source == 'inRDandH'):
distinct_companiesRD = self.filter(fileType=0).values_list('companyName', flat=True).distinct()
distinct_companiesH = self.filter(fileType=1).values_list('companyName', flat=True).distinct()
distinct_companies = set(distinct_companiesRD).intersection(set(distinct_companiesH))
else:
distinct_companies = self.values_list('companyName', flat=True).distinct()
return distinct_companies
The problem is that this code runs only once, when the code is initialised on server start, because it is part of your form class definition:
companies = RawData.objects.get_companyNames(source="inRDandH")
The solution is to make choices a callable, which is run every time the form is instantiated. define that field dynamically, in the __init__ method of the form:
def get_companies_tuple():
companies = RawData.objects.get_companyNames(source="inRDandH")
return makeTuple(companies)
class CompanySelectionForm(forms.Form):
company = forms.ChoiceField(
widget=forms.Select(attrs={'class': 'form-select'}),
choices=get_companies_tuple
)
This will now fetch the data from the database every time the form is initialised, rather than only once during startup.
I have a simple Django ForeignKey relationship between two models in postgreSQL. The logic here is the Sample object can optionally have a foreign key into a type of sample control.
from django.contrib.postgres.fields import CICharField
from django.db import models
class Sample(models.Model):
controls_applied = models.ForeignKey(SampleControl, null=True,
blank=True,
on_delete=models.SET_NULL)
class SampleControl(models.Model):
id = CICharField(max_length=200, primary_key=True)
On the admin changelist for Sample, I am trying to create filter that queries all or none of Samples that have a specific control (in this case, we'll use a SampleControl with id='runcontrol'). I'm trying to craft the specific URI string to append to my changelist url as I have other filters I'm trying to run in conjunction.
To get all samples with controls_applied= 'runcontrol', I can simply append the following string to my URL (notice the reference to the id of the foreign key):
?controls_applied__id=runcontrol
But if I want to get all samples that are not run control, it's more complicated. Wasn't sure what to do, so I decided to use 'startswith', which has a convenient 'notstartswith' companion that will do the inverse. When I use this, I see that the following works:
?controls_applied__id__startswith=runcontrol
However, the inverse
?controls_applied__id__notstartswith=runcontrol
gives me an error: Unsupported lookup 'notstartswith' for CICharField or join on the field not permitted, perhaps you meant startswith or istartswith?
Which leads to me the simple question: is there a way to specify NOT EQUALS in the query string of the URL on Django's admin site? Thank you!
I don't think admin URLs are capable of representing an exclude queryset filter, unless you create your own SimpleListFilter.
Try this in your admin.py:
class WithoutControlListFilter(admin.SimpleListFilter):
title = ('Without Control')
parameter_name = 'without_control'
def lookups(self, request, model_admin):
return (
('runcontrol', ('Run Control')),
)
def queryset(self, request, queryset):
return queryset.exclude(controls_applied=self.value())
class SampleAdmin(admin.ModelAdmin):
list_filter = (WithoutControlListFilter,)
There is a bit of info about admin filters in the Django ModelAdmin docs.
Question
How can I build a Model that that stores one field in the database, and then retrieves other fields from an API behind-the-scenes when necessary?
Details:
I'm trying to build a Model called Interviewer that stores an ID in the database, and then retrieves name from an external API. I want to avoid storing a copy of name in my app's database. I also want the fields to be retrieved in bulk rather than per model instance because these will be displayed in a paginated list.
My first attempt was to create a custom Model Manager called InterviewManager that overrides get_queryset() in order to set name on the results like so:
class InterviewerManager(models.Manager):
def get_queryset(self):
query_set = super().get_queryset()
for result in query_set:
result.name = 'Mary'
return query_set
class Interviewer(models.Model):
# ID provided by API, stored in database
id = models.IntegerField(primary_key=True, null=False)
# Fields provided by API, not in database
name = 'UNSET'
# Custom model manager
interviewers = InterviewerManager()
However, it seems like the hardcoded value of Mary is only present if the QuerySet is not chained with subsequent calls. I'm not sure why. For example, in the django shell:
>>> list(Interviewer.interviewers.all())[0].name
'Mary' # Good :)
>>> Interviewer.interviewers.all().filter(id=1).first().name
'UNSET' # Bad :(
My current workaround is to build a cache layer inside of InterviewManager that the model accesses like so:
class InterviewerManager(models.Manager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.api_cache = {}
def get_queryset(self):
query_set = super().get_queryset()
for result in query_set:
# Mock querying a remote API
self.api_cache[result.id] = {
'name': 'Mary',
}
return query_set
class Interviewer(models.Model):
# ID provided by API, stored in database
id = models.IntegerField(primary_key=True, null=False)
# Custom model
interviewers = InterviewerManager()
# Fields provided by API, not in database
#property
def name(self):
return Interviewer.interviewers.api_cache[self.id]['name']
However this doesn't feel like idiomatic Django. Is there a better solution for this situation?
Thanks
why not just make the API call in the name property?
#property
def name(self):
name = get_name_from_api(self.id)
return name
If that isnt possible by manipulating a get request where you can add a list of names and recieve the data. The easy way is to do it is in a loop.
I would recommand you to build a so called proxy where you load the articles in a dataframe/dict, save this varible data ( with for example pickle ) and use it when nessary. It reduces loadtimes and is near efficient.
Is it possible to use the library 'requests' (HTTP library for python) to post and update nested objects in django rest framework?
I made a new create method in serializers, but I can't post outside the shell, nor with the requests library or in the api webview.
My Serializers:
class QualityParameterSerializer(serializers.ModelSerializer):
class Meta:
model = QualityParameter
fields = ("id","name", "value")
class ProductQualityMonitorSerializer(serializers.ModelSerializer):
parameters = QualityParameterSerializer(many=True)
class Meta:
model = ProductQualityMonitor
fields = ("id","product_name", "area", "timeslot", "processing_line",
"updated_on",'parameters')
def create(self, validated_data):
params_data = validated_data.pop('parameters')
product = ProductQualityMonitor.objects.create(**validated_data)
for param_data in params_data:
QualityParameter.objects.create(product=product, **param_data)
return product
POST HTTP request
If I may suggest the following form for your serializer:
from django.db import transaction
class ProductQualityMonitorSerializer(serializers.ModelSerializer):
parameters = QualityParameterSerializer(many=True)
class Meta:
model = ProductQualityMonitor
fields = (
"id",
"updated_on",
"product_name",
"area",
"timeslot",
"processing_line",
"parameters",
)
def create(self, validated_data):
# we will use transactions, so that if one of the Paramater objects isn't valid
# that we will rollback even the parent ProductQualityMonitor object creation,
# leaving no dangling objects in the database
params_data = validated_data.pop('parameters')
with transaction.atomic():
product = ProductQualityMonitor.objects.create(**validated_data)
# you can create the objects in a batch, hitting the dB only once
params = [QualityParameter(product=product, **param) for param in params_data]
QualityParameter.objects.bulk_create(params)
return product
About using python requests library: you will have to pay attention to the following aspects when posting to a django back-end:
you must provide a valid CSRF token in your request; the way this is done is via csrf-token cookie;
you must provide the proper authentication headers / tokens / cookies; this is your choice, depends how you're implementing this on the DRF back-end
if this is a request from one domain to another domain, then you have to care for CORS setup.
More to the point: what have you tried already and didn't worked ?
I am working on django rest api and using class based views like :
class UserViewSet(viewsets.ModelViewSet):
queryset = Users.objects.filter(user_type_id=3)
def get_queryset(self):
queryset = self.queryset
city = self.request.QUERY_PARAMS.get('city', '')
if city!="":
queryset = queryset.filter(work_address__city__icontains=city).distinct()
return queryset
and In url.py file :
router.register(r'api/users', views.UsersViewSet)
So I want to print raw mysql query in UserViewSet. So that I can see the query.
Is it there any method to see the raw query when we would make call "api/users".
You can either install the django debug toolbar:
https://pypi.python.org/pypi/django-debug-toolbar
or use:
# at the top of the file
from django.db import connection
# and put this before return queryset
connection.queries
to output the SQL queries in the log.