Creating new model instance through views.py including args through a url - python

I am trying to create a new model instance every time a url is accessed. so far, I have the function working in my views.py, but when the new model instance is created, the fields are empty (because I have not specified what I'd like in those fields in views.)
views.py
def session_invent(self):
session = Session() # I can add field data in here, but I want to get it via the URL
session.save()
messages.success(self, f'session invented!')
return redirect('blog-home')
urls.py
path('session/invent/', views.session_invent, name="session-invent"),
models.py
class Session(models.Model):
uid = models.CharField(max_length=50)
cid = models.CharField(max_length=50)
qid = models.CharField(max_length=50)
aid = models.CharField(max_length=50)
session_date = models.DateTimeField(auto_now_add=True)
def qid_plus_aid(self):
return '{}_{}'.format(self.qid, self.aid)
def __str__(self):
return self.uid
def get_absolute_url(self):
return reverse('session-detail', kwargs={'pk': self.pk})
Ok, so here is what i am trying to pull off:
right now if i enter mywebsite.com/session/invent/ a new Session model instance is created with empty fields. Is there a way I can fill in those fields with args in the URL? For example, something like...
mywebsite.com/session/invent/?uid=test_uid&cid=test_cid&qid=test_qid&aid=test_aid
Finished Answered code:
From the answer below here is how the updated views.py should look:
def session_invent(request):
session = Session.objects.create(
uid=request.GET['uid'],
cid=request.GET['cid'],
qid=request.GET['qid'],
aid=request.GET['aid']
)
messages.success(request, f'session invented from URL!')
return redirect('blog-home')
So, If I enter the following URL, a new record is created in my database with the values in each field set from the URL:
mywebsite.com/session/invent/?uid=test_uid&cid=test_cid&qid=test_qid&aid=test_aidz

Yes, the parameters are stored in the querystring, and you can use request.GET for a dictionary-like representation of the querystring, so:
def session_invent(request):
session = Session.objects.create(
uid=request.GET['uid'],
cid=request.GET['cid'],
qid=request.GET['qid'],
aid=request.GET['aid']
)
messages.success(request, f'session invented!')
return redirect('blog-home')
This will raise a HTTP 500 in case one of the keys is missing in the request.GET. You can use request.GET.get(…) [Django-doc] to access an element with an optional default value.
A GET request is however not supposed to have side effects. It is furthermore quite odd for a POST request to have a querystring.

Related

drf user serializer return custom data

I've been trying to create a drf app and wanted to achieve a sign in view that does two things:
set's the cookies automatically
returns the url and the username of the user
the issue is specifically in the validate function inside the serializer code
views.py:
class CookieTokenObtainPairView(TokenObtainPairView):
def finalize_response(self, request, response, *args, **kwargs):
if response.data.get("refresh"):
# the cookie part works well
# the part that doesn't is in the serializer below
user = UserLoginSerializer(data=request.data)
user = user.validate(data=request.data) if user.is_valid()
response.data["user"] = user.data if user.is_valid() else user.errors
return super().finalize_response(request, response, *args, **kwargs)
serializers.py
class UserLoginSerializer(serializers.HyperlinkedModelSerializer):
password = serializers.CharField(style={"input type": "password"}, write_only=True)
#
class Meta:
model = User
fields = (
"id",
"url",
"username",
"password",
)
# read_only_fields = ("id")
def validate(self, data):
data["username"] = self["username"]
data["password"] = self["url"]
return super().validate(data)
so as you can see the validate option is trying to get the username and the url data to return it, but instead it's trying to create a new account. so maybe the validate option was not right. I researched on the drf docs but there seem to be an entirely other function called create. so I don't know how validate is not working. maybe I'm supposed to type in another function
In your validate function, you cannot access self['username'] – you can only access user data through self.instance; but, otherwise, you only can access the instance if you passed it to the serializer in a construct like:
user_serializer = UserLoginSerializer(data=request.data, instance=user_obj)
What do you need is after user login, so I recommend to you this post: Login and Register User Django Rest Framewrok; I am pretty sure you can get what you need there.

How to redirect from a CreateView of model A to another CreateView of a model B while passing key information from model A to the next view?

I am new to Django and do not know what I need to know to do this task. I am tasked with creating a web app that has two models. Model A is the employee and model B is a company that contains many employees. During signup, I have a form for model A. Once model A form is filled out, I need to pass an id from the employee to the company signup url so that when I save the company model to the table, I can make sure that the employee id is stored and so the two tables are related. How do I go about sending the employee_id to the company form page? Do I need to use some sort of redirect?
Flow: dashboard/employee_signup -> dashboard/company_signup -> completed_signup
I've looked through multiple tutorials on Django and most seem to be too simple to solve what I need done.
Here is my EmployeeSignUpView. Right now it redirects to a 'login' page. I need to instead redirect to a CompanySignUpView while passing along an employee_id. A company can't have zero employees, so the first person to signup for the company needs to be stored in the company model. The company table includes a column that stores a list of employees in that company. So a OneToMany relationship.
class EmployeeSignUpView(CreateView):
form_class = FSPEmployeeCreationForm
success_url = reverse_lazy('login')
template_name = 'employee_signup.html'
You redirect to something like
reverse('b:create_b', kwargs={"pk":a.pk}
The URL (in app 'b') is something like
url(r'create/(?P<pk>\d+)/$', b_create.as_view(), name='create_b'),
And you can pick up this parsed pk and convrt it into a full object by subclassing the dispatch method in b_create:
def dispatch( self, request, *a, **kw):
# resolve kwarg to self.a_object
self.a_object = get_object_or_404( A_model, pk = self.kwargs.get('pk','?') )
return super().dispatch( request, *a, **kw)
Doing this early means that the rest of your view code can everywhere reference self.a_object. It might be more efficient to do this only in post or form_valid, if it's not needed to (say) generate default values for the initial get of the form, or not needed until all the posted data is valid.
Instead of providing a static success_url, you can define get_success_url to return a URL that depends on the created object.
Assuming your CompanySignUpView has a URL as follows:
path('company_sign_up/<int:employee_id>/', views.CompanySignUpView.as_view(), name='company_sign_up')
then you would do:
class EmployeeSignUpView(CreateView):
form_class = FSPEmployeeCreationForm
template_name = 'employee_signup.html'
def get_success_url(self):
return reverse('company_sign_up', kwargs={'employee_id': self.object.id})
Edit
In your CompanySignUpView you can get the employee ID in the form_valid method:
class CompanySignUpView(CreateView):
...
def form_valid(self, form):
form.instance.initial_employee_id = self.kwargs['employee_id'] # or whatever the field name is
return super().form_valid(form)

non_field_errors : ["Expected a list of items but got type "dict"."]

I create realtime chat application using websocket frontend(angular) backend(Django).. I want to store messages in to db(mySql).. when I trying to store message from angular to django.. it give me error:
non_field_errors:
["Expected a list of items but got type "dict"."]
so what is wrong?
model.py
class msg(models.Model):
name = models.ForeignKey(User, on_delete=models.CASCADE)
receiver = models.CharField(max_length=20)
text = models.CharField(max_length=1200)
myDate = models.DateTimeField()
serializer.py
class MesSerializer(serializers.ModelSerializer):
name = serializers.SlugRelatedField(many=False, slug_field='name', queryset=User.objects.all())
class Meta:
model = msg
fields = '__all__'
view.py
class msg_list(APIView):
def get(self, request, format=None):
mes = msg.objects.all()
serializer = MesSerializer(mes, many=True) # convert into JSON
return Response(serializer.data)
def post(self, request, formate = None):
serializer = MesSerializer(data=request.data, many = True) #type list
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
The trouble is not with the slug. It is that you have used many = True in the view when you pass the data to the serializer, but you are in fact only sending a single message - which is why it is a dict and not a list. Remove that parameter.
def post(self, request, formate = None):
serializer = MesSerializer(data=request.data)
I recently came across a problem which is similar to the OP. Hence would like to share my experience and solution.
I have a dictionary of items, each of them unique. I wanted to use PUT to update an existing item. So I used objects.filter to fetch the object based on the name passed via JSON Request(I know I should have used pk, but I didn't because the items are unique and will remain so). Then I created a Django REST Serializer class object to save the updated object but I failed. This is because I wasn't using many = True. But when I did use it, I faced another error:
"non_field_errors": [
"Expected a list of items but got type \"dict\"."
]
So I finally removed both many=True as well objects.filter.
Instead I used objects.get. This solved the problem because objects.get returns the required object which I want to update whereas objects.filter returns a queryset object and not the actual object that I want to update. Of course objects.get will fail if I have multiple results, in which case I need to ensure there is a pk. Then again in my case objects.get will never return more than one object.
Hope this post helps someone and saves a lot of their time. :-)

Django Class Based Views keep url parameters in session

I have a django listview working fine.
Its receive url parameters to filter data.
Its paginated.
Now, I want to maintain these data along the user session. (page number and url parameters).
Example:
I'm in the products list view.
I search for 'foo'
I select page 2
And then, I click in any product detail.
The page will redirect to detail view.
When I return to product list view, I whant to keep the search argument 'foo' and selected page 2.
What is the better way to do this?
I'm using Django 2.0.6
Models.py
class Product(models.Model):
name= models.CharField(_('name'), max_length=150)
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.0)
Views.py
class ProductList(ListView):
model = Product
paginated_by = 10
def get_queryset(self):
queryset = Product.objects.all()
name = self.request.GET.get('name', None)
if name:
queryset = queryset.filter(name__icontains=name)
return queryset
Urls.py
path('products/', views.ProductList.as_view(), name='product_list'),
For this you have to put the URL as a get request so that you can fetch the get values form the URL and use them in your filter to maintain your selection like:
url/?variable=value
Then in your Django view, you can access this by request.GET.get('variable') and pass this as the context in your HTML render page then use that variable in your filter selection.
Setting variable in session:
For setting the variable in the session you can set it by:
request.session['variable'] = 'value'
and this value can be retrieve by:
if 'variable' in request.session:
variable1 = request.session['variable']
You can refer this docs.
One common trick I use to do this is to use GET parameters and save directly the entire url in session (it saves time compared to saving each individual parameter individually)
class ProductList(ListView):
model = Product
paginated_by = 10
def get_queryset(self):
self.request.session['saved_product_list_url'] = self.request.get_full_path()
....
Then you can use it like this in templates :
product list
Or like this in views :
saved_product_list_url = self.request.session.get('saved_product_list_url')
if saved_product_list_url:
return redirect(saved_product_list_url)
else:
return redirect('product_list')
Also in your filter form you should add a "reset filters" like this :
reset filters

Django REST Framework - can't override list in ListAPIView

I am using Django REST Framework to create an endpoint that will produce a PDF document. The PDF document will have information that corresponds to a particular Department. I have two desired functionalities -- to be able to download a PDF document, and to be able to preview the document within the browser.
Since the PDF document changes over time based on data that is added to the app, the document needs to be generated in real time when it is requested. As a first step, I'm trying to have the document be generated in a remote file storage location when the following endpoint is hit by a GET request:
departments/<department_pk>/result/preview
Since my endpoint should only take GET requests, I am using a ListAPIView. I'm trying to override the list method so that my custom document generation logic is executed, but it looks like the method is never called. How can I have some custom document generation logic be inserted into my endpoint, so that it is executed when the endpoint is hit by a GET request?
api/urls.py
url(r'^departments/(?P<department_pk>[0-9]+)/result/preview',
include(result_document_urls.result_document_preview_router.urls,
document_app/urls.py
result_document_preview_router = routers.DefaultRouter()
result_document_preview_router.register(r'^', ResultDocumentDetailView.as_view(),
base_name='Department')
document_app/views.py
class ResultDocumentDetailView(generics.ListAPIView):
queryset = Department.objects.all()
lookup_field = 'department_pk'
lookup_url_kwarg = 'department_pk'
def list(self, request, department_pk):
queryset = self.get_queryset()
import ipdb; ipdb.set_trace() # this break point is never hit
department = get_object_or_404(queryset, department_pk=department_pk)
...generate document logic...
return Response(status=status.HTTP_200_OK)
replace list method with below code, I think it will work
class ResultDocumentDetailView(generics.ListAPIView):
queryset = Department.objects.all()
lookup_field = 'department_pk'
lookup_url_kwarg = 'department_pk'
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
import ipdb; ipdb.set_trace() # this break point is never hit
department = get_object_or_404(
queryset, department_pk=kwargs.get('department_pk')
)
...generate document logic...
return Response(status=status.HTTP_200_OK)
for more reference see the overrinding method "list"
https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py#L35
In your document_app/urls.py, you are incorrectly passing ResultDocumentDetailView as an argument instead of a viewset.
Router while registering accepts a ViewSet instead of an APIView.
There are two mandatory arguments to the register() method:
prefix - The URL prefix to use for this set of routes.
viewset - The viewset class.
Also, since you are only interested in the retrieve method, you can just create a ResultDocumentRetrieveView and add its corresponding url to your urls.py without the need of creating a ResultDocument router. (Routers are generally used when you want to handle both list and detail requests.)
class ResultDocumentRetrieveView(generics.RetrieveAPIView):
queryset = Department.objects.all()
lookup_field = 'department_pk'
lookup_url_kwarg = 'department_pk'
def retrieve(self, request, department_pk):
department = self.get_object()
...generate document logic...
return Response(status=status.HTTP_200_OK)
urls.py
url(r'^departments/(?P<department_pk>[0-9]+)/result/preview', ResultDocumentRetrieveView.as_view())

Categories