How to pass data to a class based view's method? - django

I am trying to figure out how I can pass data from a form to the method of a class based view that serves as a API endpoint.
Homepage view (has a form to enter a stock ticker):
def home(request):
# data = get_stock_data('TSLA', key)
if request.method == 'POST':
form = TickerForm(request.POST)
if form.is_valid():
ticker = form.cleaned_data['ticker']
stock_data = get_stock_data(ticker, api_key)
return redirect('chart-data', ticker=ticker) # this line I am having trouble with
else:
form = TickerForm()
stock_data = None
return render(request, 'app/home.html', {'data': stock_data, 'form':form})
The API View:
class ChartData(APIView):
authentication_classes = []
permission_classes = []
def get(self, request, ticker, format=None):
# get stock data
stock_data = get_stock_data(ticker, api_key) # how do I pass the ticker?
labels = []
default_items = []
# get label & values
for key, value in stock_data.items():
labels.append(key)
default_items.append(value['4. close'])
# prepare data
data = {
'labels': labels[::-1],
'default_items': default_items[::-1]
}
return Response(data)
urls.py
urlpatterns = [
path('', views.home, name="homepage"),
path('api/chart/data', views.ChartData.as_view(), name="chart-data"),
]
Then I get the data with Javascript and display the graph on the frontend, which works fine. The only thing I can't figure out how to pass the ticker argument to the get method of my ChartData view. I hope my problem is clear.

return redirect('chart-data', {'ticker':ticker})
Have to use a dictionary, I believe ...

You should include ticker in the url path.
path('api/chart/data/<slug:ticker>/', views.ChartData.as_view(), name="chart-data"),
This assumes ticker is a string like 'TSLA'.

Related

Passing Form Querystring (custom queryset) to Django Detailview

I have a form:
class MyTestForm(forms.Form):
options = (
("1", "deck1"),
("2", "deck2"),
("3", "deck3"),
("4", "deck4"),
)
choices = forms.MultipleChoiceField(choices = options, widget=forms.CheckboxSelectMultiple, required = False)
and a views.py which passes the querystring of the form:
class NewFormView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
form = MyNewForm()
context = {'form':form }
return render(request, 'testformv2.html', context)
def post(self, request, *args, **kwargs):
form = MyNewForm(data=request.POST)
if form.is_valid():
data=form.cleaned_data
base_url = reverse('newcombined', kwargs={'pk':24}) ## change this
querystring = '{}?'.format(base_url)
myqueries = [query for query in data if data[query] != []] # take the non-empty selections of the form
for query in myqueries:
if len(data[query]) == 1:
query_arguement = str(query)+'='+str(data[query][0]) +'&'
querystring += query_arguement
elif len(data[query])>1:
query_argument = str(query)+'='+','.join(data[query])+'&'
querystring += query_argument
else:
print('error occured for some reason')
url = querystring[:-1] #takes the last character & sign off
return redirect(url)
return render(request, 'testformv2.html', {'form':form})
so i will end up with a url that looks like "/v2/test/25/?Decks=2,4" ... my problem is I want to use the querystring filters to filter for a specific queryset, however, i must provide DetailView with a primary key in the URL. I am wondering if i should be processing the queryset in the post request of the form (when the form is submitted), OR if i should get the primary key of one of the objects in the queryset, and THEN get the custom filtered queryset in the DetailView. (my problem is more complicated than this because i have a form on the detailview which uses get_object() function, so i cannot use ListView.

Django, getting data in different page from view

Hello I am new to Django, I am currently working on a project but I can`t figure out how I should do something.
Right now I am at the page
home/stats/ID/LANGUAGE
but inside this page I want a "edit" button, once clicked I want to go to:
home/stats/ID/LANGUAGE/edit/
I just want to get the same data from home/stats/ID/LANGUAGE again, but now in home/stats/ID/LANGUAGE/edit/
My view.py:
class StatsView(TemplateView):
template_name = 'home/stats.html'
analytics = build('analyticsreporting', 'v4', credentials=credentials)
def get(self, request, id, language):
min_date = "2018-12-01"
date01 = datetime.strptime(min_date, '%Y-%m-%d')
max_date = "2018-12-31"
date02 = datetime.strptime(max_date, '%Y-%m-%d')
print(date01)
print(date02)
if request.GET.get('date1'):
date1 = request.GET.get('date1')
pol1 = datetime.strptime(date1, '%Y-%m-%d')
date01 = pol1
print(date01)
if request.GET.get('date2'):
date2 = request.GET.get('date2')
pol2 = datetime.strptime(date2, '%Y-%m-%d')
date02 = pol2
print(date02)
if request.user.is_authenticated:
current_user = request.user.id
result = BlablaAuth.objects.filter(user=request.user)
if language == 'ALL':
blabla = Blabla.objects.filter(blabla=id)
prefix = '/blabla/' + id
if result and blabla.count() > 0:
analytics_result1 = self.analytics.reports().batchGet(
body={
"Google analytics reporting stuff"
analytics_result2 = self.analytics.reports().batchGet(
body={
"Google Reporting stuff"
return render(request, self.template_name, context={
"report1": analytics_result1.execute(),
"report2": analytics_result2.execute()
})
else:
apple = Apple.objects.filter(video=id)
prefix = '/apple/' + id
if result and apple.count() > 0:
analytics_result1 = self.analytics.reports().batchGet(
body={
"Google reporting analytics stuff"
analytics_result2 = self.analytics.reports().batchGet(
body={
"Google reporting analytics stuff"
return render(request, self.template_name, context={
"report1": analytics_result1.execute(),
"report2": analytics_result2.execute()
})
My urls.py:
from django.conf.urls import url
from django.contrib import admin
from home.views.views import HomeView, StatsView
from .views import views
from django.contrib.auth.decorators import login_required
app_name = "home"
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^stats/(?P<id>[0-9]+)/(?P<language>[a-zA-Z]+)/$',
login_required(StatsView.as_view()), name='stats'),
url(r'^stats/(?P<id>[0-9]+)/(?P<language>[a-zA-Z]+)/edit/$',
StatsView.edit_stats, name='stats_edit'),
url(r'^$', login_required(HomeView.as_view()), name='home'),
]
My button in stats.html:
<button><a href="{% url home:stats_edit auth.blabla.id apple.language %}">Edit</button>
Assuming your edit view will be based on a generic CBV (e.g. UpdateView), you can create a Mixin class that has a method get_context_data(self, **kwargs) and does all the stuff you now do in the get() method of your TemplateView. This method will automatically get called by your TemplateView and UpdateView and add the context to your rendering.
class AnalyticsMixin(object):
analytics = None # or some default that can be used by all subclasses.
# None is not a good default since it will raise an AttributeError when calling self.analytics.reports()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# all the stuff you do in get() method, using self.request and self.kwargs which are set by the `as_view()` method on the CBV
request = self.request
id = self.kwargs.get('id')
language = self.kwargs.get('language')
...
return context.update({
"report1": analytics_result1.execute(),
"report2": analytics_result2.execute()
})
then in your views:
class StatsView(AnalyticsMixin, TemplateView):
template_name = ...
analytics = ... # override here or if it depends on the request override in a method
# remove the get() method unless you do something that's not for both views.
# e.g. to override self.analytics if credentials depends on request
def get_context_data(self, **kwargs):
self.analytics = build('analyticsreporting', 'v4', credentials=self.get_credentials())
return super().get_context_data(**kwargs)
and for your edit view:
class EditStatsView(AnalyticsMixin, UpdateView):
template_name = ...
model = ... # name of model to update
# here add the form_class for the editing form if you customise it
# the context will have the form + the data from get_context_data()
way to pass data between two view is session . django support authenticate as well as anonymous session. just store the data as session key and retrieve it when ever you need it.

django rest framework create json and csv api

i am trying to create 2 api's. 1 to access json using ajax datatable, and the other to create an export csv button.
the json api i manage to create and get the data from it using ajax, but i get problems when i try to access the api in the browser and the other to add the csv api..i will show you part of my scripts and the errors i get.
views.py
class VideoViewSet(viewsets.ModelViewSet):
queryset = DatamsVideos.objects.all()
serializer_class = VideosSerializer
parser_classes = (CSVParser,) + tuple(api_settings.DEFAULT_PARSER_CLASSES)
renderer_classes = (CSVRenderer,) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
def list(self, request, **kwargs):
try:
vs = query_vs_by_args(**request.query_params)
serializer = VideosSerializer(vs['items'], many=True)
result = dict()
result['data'] = serializer.data
result['draw'] = vs['draw']
result['recordsTotal'] = vs['total']
result['recordsFiltered'] = vs['count']
return Response(result, status=status.HTTP_200_OK, template_name=None, content_type=None)
except Exception as e:
return Response(e, status=status.HTTP_404_NOT_FOUND, template_name=None, content_type=None)
def get_renderer_context(self):
context = super(VideoViewSet, self).get_renderer_context()
context['header'] = (
self.request.GET['fields'].split(',')
if 'fields' in self.request.GET else None)
return context
#action(methods=['GET'], detail=False)
def bulk_upload(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_303_SEE_OTHER, headers={'Location': reverse('videos-api')})
urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'videos', VideoViewSet)
urlpatterns = [
url(r'^$', index, name='main'),
url(r'^api/', include(router.urls)),
]
and this is part of my models.py
def query_vs_by_args(**kwargs):
draw = int(kwargs.get('draw', None)[0])
length = int(kwargs.get('length', None)[0])
start = int(kwargs.get('start', None)[0])
search_value = kwargs.get('search[value]', None)[0]
order_column = kwargs.get('order[0][column]', None)[0]
order = kwargs.get('order[0][dir]', None)[0]
order_column = ORDER_VS_COLUMN_CHOICES[order_column]
# django orm '-' -> desc
if order == 'desc':
order_column = '-' + order_column
queryset = DatamsVideos.objects.all()
total = queryset.count()
if search_value:
queryset = queryset.filter(Q(link__icontains=search_value) |
Q(title__icontains=search_value) |
Q(views__icontains=search_value) | Q(date_added__icontains=search_value))
count = queryset.count()
queryset = queryset.order_by(order_column)[start:start + length]
return {
'items': queryset,
'count': count,
'total': total,
'draw': draw
}
1 of the problem i have is with the function list from views.py at
vs = query_vs_by_args(**request.query_params)
serializer = VideosSerializer(vs['items'], many=True)
i still manage to get the data from it using ajax, but when i try to access the api on the browser /main/api/videos/ it gives me this error:
HTTP 404 Not Found
Allow: GET, POST, HEAD, OPTIONS
Content-Type: text/csv ;utf-8
Vary: Accept
""
'NoneType' object has no attribute '__getitem__'
Same when i try to access /main/api/videos/?format=json
TypeError("'NoneType' object has no attribute '__getitem__'",) is not JSON serializable
When i try to get the csv from /main/api/videos/?format=csv, It Tells me File Not Found
I might be missing some details, can't just let me know if you want more information.
Thank you in advance
UPDATE
Now i just realize if i comment the function list the api's works fine..but my ajax is not working anymore because i don't have the 4 necessary things for to build the json
result['data'] = serializer.data
result['draw'] = ch['draw']
result['recordsTotal'] = ch['total']
result['recordsFiltered'] = ch['count']
I manage to fix the csv and api problem, by separating the classes.
class VideoCSVExportView(viewsets.ModelViewSet):
queryset = DatamsPornhubVideos.objects.all()
serializer_class = VideosSerializer
renderer_classes = (CSVRenderer,) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
after i create this new class for csv, i put it on a different route.
csv_router = DefaultRouter()
csv_router.register(r'videos', VideoCSVExportView)
...
url(r'^csv/', include(csv_router.urls)),
..

Django - Combining a datatable view and form view

I am trying to put a table from datatables and a crispy form into one view so that I can display it on a single page in django. I am completely stuck on how to combine them as i'm still a newbie.
Any help is greatly appreciated!!
Views.py ------------- Datatable below
class CustomerTable(XEditableDatatableView):
template_name = "customers.html"
model = Customer
datatable_options = {
'columns': [
("Title", 'Title'),
("Name", 'PoC', helpers.make_xeditable),
("Email", 'PoCEmail', helpers.make_xeditable),
("Location", 'Location', helpers.make_xeditable),
("DateAdded", 'DateAdded', helpers.make_xeditable),
],
'hidden_columns': ['ID'],
}
-----------Crispy Form below
def CustomerView(request):
form = CustomersForm(request.POST or None)
success = False
if request.method == 'POST':
if form.is_valid():
form.save()
form = CustomersForm()
success = True
if request.POST.get('delete'):
obj.delete()
customer_form = CustomersForm()
return render(request, 'customers.html', {'customer_form': customer_form})
urls.py ----------------
urlpatterns = patterns('',
url(r'^$', views.CustomerTable.as_view(), name='customertable'),
url(r'^$', 'ISL.views.CustomerView', name='customersform'),
)
Thanks for any help!
W
You can try to use get_context_data() method of your class like this:
def get_context_data(self, **kwargs):
context = super(CustomerTable, self).get_context_data(**kwargs)
# do your magic here <<<
# then
context['customer_form'] = customer_form
return context
First you are calling ancestor's get_context_data method via super built-in function, then you can append to context everything you want, in your example it is custom form. After this, you can access 'custom_form' in your template.
Unfortunately, i cant test it at the moment, but this method must be appended in ancestor by ContextMixin (django doc about this method: https://docs.djangoproject.com/en/1.8/ref/class-based-views/mixins-simple/).

tastypie, GET/POST a field of a model?

I have a model like below.
class Checklist(models.Model):
name = models.CharField(max_length=50, default="mylist")
items = JSONField(default=get_default_checklist)
user = models.ForeignKey(User, related_name='checklists')
For a given Checklist.id, I want to get items field only, so I created a resource for it.
class ChecklistItemsResource(ModelResource):
def dehydrate_items(self, bundle):
return json.dumps(bundle.obj.items, ensure_ascii=False)
class Meta:
queryset = models.Checklist.objects.all()
resource_name = 'checklist_items'
fields = ['items']
and I get the data with url /api/v1/checklist_items/8/?format=json
id=8 is actually id of checklist not id of checklist.items.
edit -
I think /api/v1/checklist/8/items/ looks better than /api/v1/checklist_items/8/.
To represent items field of checklist(id=8).
How do you create resource/url to fetch/update a specific field of a model?
You could use the prepend_urls hook to create a /items/ subresource for your Checklist resource. Add the following to your resource:
from django.conf.urls import url
from tastypie.bundle import Bundle
from tastypie.utils import trailing_slash
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<%s>\w[\w/-]*)/items%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash()), self.wrap_view('get_items'), name="api_get_items"),
]
def get_items(self, request, **kwargs):
pk = kwargs[self._meta.detail_uri_name]
try:
checklist = Checklist.objects.get(pk=pk)
except Checklist.DoesNotExist:
return self.create_response(request, {}, status=404)
if request.method == 'GET':
bundle = Bundle(request=request, obj=checklist)
bundle.data['items'] = self._meta.fields['items'].dehydrate(bundle)
if hasattr(self, 'dehydrate_items'):
bundle.data['items'] = self.dehydrate_items(bundle)
return self.create_response(request, bundle)
elif request.method == 'PATCH':
data = self.deserialize(request, request.body, format=request.META.get('CONTENT_TYPE', 'application/json'))
checklist.items = data
checklist.save()
return self.create_response(request, {})
To update the items field, send a PATCH request to the /items/ endpoint with new serialized new value in the body. This view can be easily extended for general case.