expected str, bytes or os.PathLike object, not BoundField in instaapi - django

I was trying to post a picture in instagram, I have picture url, caption, instagram username and password in 3 different model, I'm sending primary key of the models in response.
class InstagramPost(APIView):
#staticmethod
def post(request, user_id, pk, attachment_id, *args, **kwargs):
user_name = get_user_name(user_id)
caption = get_post(pk)
url = get_attachment(attachment_id)
bot = Bot()
bot.login(username=user_name['name'], password=user_name['password'], is_threaded=True)
print(user_name['name'])
print(user_name['password'])
bot.upload_photo(url[r'url'], caption=caption['caption'])
print(url['url'])
print(caption['caption'])
return HttpResponse("posted")

Related

Saving form data from a FormView to session

I am trying to capture the POST request data for each field and store it in the session so that I can use the data in another view. But I am getting errors in the other view because the session variables are returning 'None'.
Before, I had written the code using a non-class based view and fetched the values with request.POST.get('name') and this worked. What should I be doing here?
class TripsView(FormView):
""" A view to show all trips and receive trip search data """
template_name = "products/trips.html"
form_class = SearchTripsForm
def form_valid(self, form):
"""
Takes the POST data from the SearchTripsForm and stores it in the session
"""
trip_choice = form.cleaned_data["destination"].id
self.request.session["destination_choice"] = trip_choice
self.request.session["searched_date"] = "26-12-2021"
self.request.session["passenger_total"] = form.cleaned_data[
"passengers"
]
return super(TripsView, self).form_valid(form)
def get_context_data(self, **kwargs):
""" Adds to the context the Product objects categorized as trips """
context = super().get_context_data(**kwargs)
context["destinations"] = Product.objects.filter(category=3)
return context
def get_success_url(self):
""" Overides the success url when the view is run """
return reverse("selection")
My other view is as follows:
class SelectTripView(View):
"""
Provides the user a set of choice options based on their search input in
products.TripsView
"""
template_name = "bookings/trips_available.html"
form_class = DateChoiceForm
def get(self, request):
"""
Initialises the DateChoiceForm with data from SearchTripsForm
& render to the template
"""
searched_date = self.request.session["searched_date"]
print(searched_date)
naive_searched_date = datetime.strptime(searched_date, "%Y-%m-%d")
gte_dates = self.trips_matched_or_post_date(searched_date)
lt_dates = self.trips_preceding_date(searched_date)
... etc etc...
The code flags an error here at the datetime.strptime() method because it says argument 1 must be a string not a tuple - (None)
Update:* I have located that the problem is because the date string is being converted to a tuple (2,0,2,1 etc..) - maybe because it includes punctuation dashes '-' between Y-M-D? Is there a workaround for this? That being said, the string was just a placeholder. In the real case, I am pulling the value from form data which gives me date object and would need to be serialized before sending to the session.
I am confused as to why using request.POST.get() to retrieve the form data in non classed based View did not encounter such errors. Is there a difference to the data once it is cleaned_data?

Login user with a login link

I want to send a login link to the users.
I know there are some OneTimePassword apps out there with thousands of features. But I just want some easy and barebon way to login user via login link.
My question is if this is a correct way to go about this. Like best practice and DRY code.
So I've set up a table that stores three rows.
1. 'user' The user
2. 'autogeneratedkey' A autogenerated key
3. 'created_at' A Timestamp
When they login, the'll be sent a mail containing a login link valid for nn minutes.
So the login would be something like
https://example.net/login/?username=USERNAME&autogeneratedkey=KEY
The tricky part for me is to figure out a good way to check this and log in the user.
I'm just guessing here. But would this be a good approach?
class login(generic.CreateView):
def get(self, request, *args, **kwargs):
try:
autgeneratedkey = self.request.GET.get('autgeneratedkey', '')
username = self.request.GET.get('username', '')
obj_key = Login.objects.filter(autgeneratedkey=autgeneratedkey)[0]
obj_user = Login.objects.filter(userusername=username)[0]
try:
if obj_user == obj_key: #Compare the objects if same
if datetime.datetime.now() < (obj_key.created_at + datetime.timedelta(minutes=10)): #Check so the key is not older than 10min
u = CustomUser.objects.get(pk=obj_user.user_id)
login(request, u)
Login.objects.filter(autgeneratedkey=autgeneratedkey).delete()
else:
return login_fail
else:
return login_fail
except:
return login_fail
return redirect('index')
def login_fail(self, request, *args, **kwargs):
return render(request, 'login/invalid_login.html')
It feels sloppy to call the same post using first the autogeneratedkey then using the username. Also stacking if-else feels tacky.
I would not send the username in the get request. Just send an autogenerated key.
http://example.com/login?key=random-long-string
Then this db schema (it's a new table because I don't know if Login is already being used.
LoginKey ( id [PK], user [FK(CustomUser)], key [Unique], expiry )
When a user provides an email, you create a new LoginKey.
Then do something like this:
def get(self, request, *args, **kwargs):
key = request.GET.get('key', '')
if not key:
return login_fail
login_key = LoginKey.objects.get(key=key)
if login_key is None or datetime.datetime.now() > login_key.expiry:
return login_fail
u = login_key.user
login(request, u)
login_key.delete()
return redirect('index')
Probably you can optimize the code like this:
First assuming you have relationship between User and Login Model like this:
class Login(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
Then you can use a view like this:
class LoginView(generic.View):
def get(self, request, *args, **kwargs):
try:
autgeneratedkey = self.request.GET.get('autgeneratedkey', '')
username = self.request.GET.get('username', '')
user = CustomUser.objects.get(login__autgeneratedkey=autgeneratedkey, username=username, login__created_at__gte=datetime.now()-datetime.timedelta(minutes=10))
login(request, user)
user.login_set.all().delete() # delete all login objects
except CustomUser.DoesNotExist:
return login_fail
return redirect('index')
Just another thing, it is not a good practice to use GET method where the database is updated. GET methods should be idempotent. Its better to use a post method here. Just allow user to click the link(which will be handled by a different template view), then from that template, use ajax to make a POST request to this view.

How to access request url pk from throttle class?

I am a newbie to Django, and I am building a Django application that uses REST API. I have a throttle class, and I want to restrict users to send more than 5 number of invitations to the same user in one minute. I send a user ID with the URL.
I should access pk to use it for the caching operation. How can I access the pk from the throttle class?
For example: api/users/3299143165471965406/resend-invitation-email/
Pk will be: 3299143165471965406
views.py
#decorators.action(methods=['post'], detail=True, serializer_class=None,
permission_classes=[core_permissions.IsCompanyAdmin],
url_path='resend-invitation-email', throttle_classes=[throttles.ResendInvitationThrottle])
def resend_invitation_email(self, request, pk=None):
user = get_object_or_404(User, pk=pk)
if user.invitation_status == User.INVITATION_STATUS_ACCEPTED or user.invitation_status is None:
raise ValidationError("This user is already registered.")
else:
invitations_tasks.send_invitation_email.delay(pk)
return response.Response(status=200)
throttle:
class ResendInvitationThrottle(SimpleRateThrottle):
scope = 'invitation'
def get_cache_key(self, request, view):
invited_user_id = 1 # Here I should use PK
return self.cache_format % {
'scope': self.scope,
'ident': invited_user_id
}
Edit:
I solved it by using: invited_user_id = view.kwargs['pk']

Looking for format for KeywordsField.save_form_data

I have a Mezzanine Project and am trying to update the keywords on a blog entry. I am having difficulty getting the format correct to call KeywordsField.save_form_data this invokes a js that will update the keywords on a blog post. See below:
From Messanine/generic/fields.py
class KeywordsField(BaseGenericRelation):
"""
Stores the keywords as a single string into the
``KEYWORDS_FIELD_NAME_string`` field for convenient access when
searching.
"""
default_related_model = "generic.AssignedKeyword"
fields = {"%s_string": CharField(editable=False, blank=True,
max_length=500)}
def __init__(self, *args, **kwargs):
"""
Mark the field as editable so that it can be specified in
admin class fieldsets and pass validation, and also so that
it shows up in the admin form.
"""
super(KeywordsField, self).__init__(*args, **kwargs)
self.editable = True
def formfield(self, **kwargs):
"""
Provide the custom form widget for the admin, since there
isn't a form field mapped to ``GenericRelation`` model fields.
"""
from mezzanine.generic.forms import KeywordsWidget
kwargs["widget"] = KeywordsWidget
return super(KeywordsField, self).formfield(**kwargs)
def save_form_data(self, instance, data):
"""
The ``KeywordsWidget`` field will return data as a string of
comma separated IDs for the ``Keyword`` model - convert these
into actual ``AssignedKeyword`` instances. Also delete
``Keyword`` instances if their last related ``AssignedKeyword``
instance is being removed.
"""
from mezzanine.generic.models import Keyword
related_manager = getattr(instance, self.name)
# Get a list of Keyword IDs being removed.
old_ids = [str(a.keyword_id) for a in related_manager.all()]
new_ids = data.split(",")
removed_ids = set(old_ids) - set(new_ids)
# Remove current AssignedKeyword instances.
related_manager.all().delete()
# Convert the data into AssignedKeyword instances.
if data:
data = [related_manager.create(keyword_id=i) for i in new_ids]
# Remove keywords that are no longer assigned to anything.
Keyword.objects.delete_unused(removed_ids)
super(KeywordsField, self).save_form_data(instance, data)
From my Views.py
class PubForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['keywords']
def UpdatePub(request, slug):
blog_post = BlogPost.objects.get(id=slug)
if request.method == 'POST':
form = PubForm(request.POST)
if form.is_valid():
publish_date = datetime.datetime.now()
blog_post.status = CONTENT_STATUS_PUBLISHED
publish_date=publish_date
tags=form.cleaned_data['keywords']
blog_post.save()
KeywordsField.save_form_data(user,blog_post,tags)
return HttpResponseRedirect('/write/')
else:
form = PubForm(instance=blog_post)
return render(request, 'blog_my_pub.html', {'form' : form})
It complains that the field 'user' has no attribute 'name'. I have tried many different values for this parameter and cannot figure it out. Any help would be appreciated.
Thanks for any input.

Django 1.11: "global name 'user' is not defined"

I have a survey app - you create a Survey and it saves the Response. It's registered in Django Admin. I can see the Survey and submit a Response. When I click Response in Admin, I get the following error:
ValueError at /admin/django_survey/response/
Cannot query "response 5f895af5999c49929a522316a5108aa0": Must be "User" instance.
So I checked the SQL database and for django_survey_response I can see that there is a response, but the column user_id is NULL.
I suspected that there's an issue with my Views and/or Forms and I'm not saving the logged in User's details, so I've tried to address that.
However, now I get
NameError at /survey/1/
global name 'user' is not defined
How do I resolve this? I want the form to save Response with the logged in user's ID.
The Traceback:
django_survey\views.py
def SurveyDetail(request, id):
survey = Survey.objects.get(id=id)
category_items = Category.objects.filter(survey=survey)
categories = [c.name for c in category_items]
print 'categories for this survey:'
print categories
if request.method == 'POST':
form = ResponseForm(request.POST, survey=survey) <.........................
if form.is_valid():
response = form.save()
return HttpResponseRedirect("/confirm/%s" % response.interview_uuid)
else:
form = ResponseForm(survey=survey)
print form
django_survey\forms.py
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
self.survey = survey
self.user = user <.........................
super(ResponseForm, self).__init__(*args, **kwargs)
self.uuid = random_uuid = uuid.uuid4().hex
# add a field for each survey question, corresponding to the question
# type as appropriate.
data = kwargs.get('data')
It might be worth noting that previously, instead of user, the model's field was called interviewee. I changed this and ran migrations again.
I am also using userena.
The error message in this instance is python trying to tell you that you are attempting to access a variable user that has not been defined in the scope of your method.
Let's look at the first few lines of the __init__() method:
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
self.survey = survey
self.user = user
We can see where the survey variable is defined: survey = kwargs.pop('survey'). It is passed into the form as a keyword argument and extracted in the forms __init__. However underneath you attempt to do the same thing with user but haven't actually defined it above. The correct code would look like:
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
user = kwargs.pop('user')
self.survey = survey
self.user = user
However, this still won't work because we aren't passing the user variable to the form via kwargs. To do that we pass it in when we initialise the form in your views.py. What isn't clear is what user object you are expecting to pass in. the request.user? or does the Survey object have a user attribute? in which case you would not need to pass user in and would just use survey.user etc.
django_survey\views.py
def SurveyDetail(request, id):
survey = Survey.objects.get(id=id)
category_items = Category.objects.filter(survey=survey)
categories = [c.name for c in category_items]
print 'categories for this survey:'
print categories
if request.method == 'POST':
form = ResponseForm(request.POST, survey=survey, user=request.user)
if form.is_valid():
response = form.save()
return HttpResponseRedirect("/confirm/%s" % response.interview_uuid)
else:
form = ResponseForm(survey=survey, user=request.user)
print form
In your view when you initialize your form you need to pass it the user (current user in this case)? similar to this form = ResponseForm(request.POST, survey=survey, user=request.user). Then in the __init__ of your form pop the user object user = kwargs.pop('user'). I believe that will resolve your issue.