GAE python: Get and post request working without submitting a post request through a form - google-app-engine

This is a continuation of this question
What follows is an abbreviated version of the original code. I tried to include the most relevant parts and left out the part of the script used by a cron job which updates the Datastore with values.
Then, in the sendFollowUp() handler, a second cron job queries the Datastore for these values, then uses a push task queue to send these values as parameters which are ultimately used in a REST API call to another service that sends people(entities) in the Datastore an email.
What I can't figure out is how to follow-up a get request with a post request in the same handler without submitting a post request through a form. This needs to happen within the sendFollowUp handler. Most of the examples I have found include submitting a form. However, I don't want to do that. I just want it to work automatically with the cron job and task queue.
import webapp2
import datetime
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.api import taskqueue
import jinja2
import os
jinja_environment = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
class emailJobs(db.Model):
""" Models an a list of email jobs for each user """
triggerid = db.StringProperty() #Trig id
recipientid_po = db.StringProperty() # id
recipientlang = db.StringProperty() #Language
fu_email_sent = db.DateTimeProperty()
fuperiod = db.IntegerProperty() # (0 - 13)
fu1 = db.DateTimeProperty()
fu2 = db.DateTimeProperty()
#classmethod
def update_fusent(cls, key_name, senddate):
""" Class method that updates fu messages sent in the GAE Datastore """
emailsjobs = cls.get_by_key_name(key_name)
if emailsjobs is None:
emailsjobs = cls(key_name=key_name)
emailsjobs.fu_email_sent = senddate
emailsjobs.put()
def timeStampFM(now):
d = now.date()
year = d.year
month = d.month
day = d.day
t = now.time()
hour = t.hour
minute = t.minute + 5
second = t.second
today_datetime = datetime.datetime(year, month, day, hour, minute, second)
return today_datetime
class MainPage(webapp2.RequestHandler):
""" Main admin login page """
def get(self):
if users.get_current_user():
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
urla = '/'
url_admin = ""
if users.is_current_user_admin():
url = users.create_logout_url(self.request.uri)
urla = "_ah/admin/"
url_admin = 'Go to admin pages'
url_linktext = 'Logout'
else:
url = users.create_login_url(self.request.uri)
url_linktext = 'Login'
template_values = {
'url': url,
'url_linktext': url_linktext,
'url_admin': url_admin,
'urla': urla,
}
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render(template_values))
class sendFollowUp(webapp2.RequestHandler):
""" Queries Datastore for fu dates that match today's date, then adds them to a task queue """
def get(self):
now = datetime.datetime.now()
now_dt = now.date() #today's date to compare with fu dates
q = emailJobs.all()
q.filter('fuperiod >', 0)
q.filter('fuperiod <', 99)
for part in q:
guid = str(part.recipientid_po)
lang = str(part.recipientlang)
trigid = str(part.triggerid)
if part.fuperiod == 1:
fu1rawdt = part.fu1
fu1dt = fu1rawdt.date()
if fu1dt == now_dt:
follow_up = '1'
if part.fuperiod == 2: # the values go up to 12 in the original code
fu2rawdt = part.fu2
fu2dt = fu2rawdt.date()
if fu2dt == now_dt:
follow_up = '2'
if follow_up != None:
taskqueue.add(queue_name='emailworker', url='/emailworker', params={'guid': guid,
'fu': follow_up,
'lang': lang,
'trigid': trigid,
})
self.redirect('/emailworker')
class pushQueue(webapp2.RequestHandler):
""" Sends fu emails, updates the Datastore with datetime sent """
def store_emails(self, trigid, senddate):
db.run_in_transaction(emailJobs.update_fusent, trigid, senddate)
def get(self):
fu_messages = {'1': 'MS_x01',
'2': 'MS_x02',
# the values go up to 12 in the original code
}
langs = {'EN': 'English subject',
'ES': 'Spanish subject',
}
fu = str(self.request.get('fu'))
messageid = fu_messages[fu]
lang = str(self.request.get('lang'))
subject = langs[lang]
now = datetime.datetime.now()
senddate = timeStampFM(now)
guid = str(self.request.get('guid'))
trigid = str(self.request.get('trigid'))
data = {}
data['Subject'] = subject
data['MessageID'] = messageid
data['SendDate'] = senddate
data['RecipientID'] = guid
# Here I do something with data = {}
self.store_emails(trigid, senddate)
app = webapp2.WSGIApplication([('/', MainPage),
('/cron/sendfu', sendFollowUp),
('/emailworker', pushQueue)],
debug=True)

I'm not sure I really understand your question, but can't you just create a POST request with the requests module?
Post Requests
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post("http://httpbin.org/post", data=payload)
Also for easy task use have you seen the deferred library?
The deferred library lets you bypass all the work of setting up dedicated task handlers and serializing and deserializing your parameters by exposing a simple function, deferred.defer(). To call a function later, simply pass the function and its arguments to deferred.defer
Link

Related

Using Django formset displaying related names in template

I am trying to display the state values for each country name in Django app. To save the user response, I am using Django generic CreateView. My models look something like this:
class Question(model.Models):
ques_id = models.AutoField(primary_key=True)
country = models.ForeignKey(Country)
state = models.CharField(max_length=...)
class Test(model.Models):
test = models.AutoField(primary_key=True, )
test_num = models.CharField(max_length=6, )
class Response(model.Models):
response = models.AutoField(primary_key=True)
test_id = models.ForeignKey(Test, related_name='test', )
ques_offered = models.ForeignKey(Question, related_name='choice',
ans_submitted = models.CharField(max_length=240,
To display the available choices for field state for each country value (in the db), I am looping through Django management form for the formset in my template. However, I am unable to get to the values of field state instead I am getting the country values.
Additional info:
The views that I am using to achieve this:
class ResponseCreateView(CreateView):
template_name = ...
model = Test
form_class = # Form_name
def get_context_data(self, **kwargs):
data = super(ResponseCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['get_response'] = responseFormset(self.request.POST, self.request.FILES)
else:
data['get_response'] = responseFormset()
def form_valid(self, form):
context = self.get_context_data()
get_response = context['get_response']
with transaction.atomic():
if get_response.is_valid():
self.object = form.save()
get_response.instance = self.object
get_response.save()
return redirect('...')
else:
context.update({'get_response': get_response,
})
return self.render_to_response(context)
return super(ResponseCreateView, self).form_valid(form)

Uploading data to user's page in Django using Forms

I'm trying to code the page so that every user has its userpage where they can add their own info ( in this case YCD data). Now I'm trying to code the add button, with which user will be able to add a note to its personal page.
def add_YCD(request):
current_user = request.user
current_profile = Profile.objects.get(user_id = current_user.id)
if request.method == "POST":
if current_user.is_authenticated:
YCD_form = YCDForm(request.POST, instance = current_profile)
if YCD_form.is_valid():
YCD_form.save()
messages.success(request,('Your profile was successfully updated!'))
else:
messages.error(request,('Unable to complete request'))
return redirect("main:homepage")
YCD_form = YCDForm(instance = current_profile)
return render(request = request,
template_name = "main/add_YCD.html",
context = {"YCD_form": YCD_form,
"user": request.user,})
The code works without instance = current_profile, it just saves the note to database but doesn't display it on the userpage. I've also tried using instance = request.user.profile.
But it doesn't work at all.
Here are the Models themselves:
class Yield_Curve_Data(models.Model):
b1 = models.BigIntegerField()
b2 = models.BigIntegerField()
b3 = models.BigIntegerField()
tau = models.BigIntegerField()
Date = models.DateTimeField('date published', default = datetime.now)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
yield_curve_data = models.ManyToManyField(Yield_Curve_Data, null = True)
#receiver(post_save, sender = User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender = User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
And here's the code for forms:
class YCDForm(forms.ModelForm):
class Meta:
model = Yield_Curve_Data
fields =('b1', 'b2', 'b3', 'tau',)
Is there another way to specify the user itself or o I need to change the code completely?
Thanks

Django-3.1/DRF/React: Unable to save nested images (linked through GenericRelation)

I am building a Django+DRF/React app (simple blog app) and i am facing difficulties saving nested images
Model Structure
Model:
Post
Children:
details: ContentType Model ( DRF: save is successfull )
images: ContentType Model ( DRF : save is not successfull )
Process
Send images from <input type="file" multiple />
Process data through FormData
Catch request.data and process it
class PostFormView(generics.RetrieveUpdateDestroyAPIView):
queryset = Post._objects.is_active()
serializer_class = PostModelSerializer
permission_classes = (IsOwnerOr401,)
parser_classes = (parsers.MultiPartParser,parsers.JSONParser,
parsers.FormParser, parsers.FileUploadParser)
lookup_field = 'slug'
lookup_url_kwarg = 'slug'
def get_queryset(self):
return super().get_queryset().annotate(**sharedAnnotations(request=self.request))
def update(self, request, *args, **kwargs):
data = request.data
_images = data.getlist('images')
images = []
for _ in _images:
if isinstance(_, dict):
images.append(images)
continue
images.append({'image': _, 'object_id': self.get_object().pk, 'content_type': self.get_object().get_content_type().pk})
data['images'] = images
print(data)
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
instance._prefetched_objects_cache = {}
return Response(serializer.data)
Save images (FAIL):
class MediaModelSerializer(ContentTypeModelSerializer):
# inherits object_id & content_type fields just to avoid writing them over and over alongside (create & update fns)
class Meta:
model = Media
fields='__all__'
class PostModelSerializer(WritableNestedModelSerializer):
is_active = serializers.BooleanField(default=True)
path = serializers.HyperlinkedIdentityField(
view_name="api:post-detail", lookup_field='slug')
images = MediaModelSerializer(many=True)
details = DetailModelSerializer(required=False, many=True)
# annotated fields
is_author = serializers.BooleanField(read_only=True, default=False)
class Meta:
model = Post
fields = '__all__'
read_only_fields = ['is_locked', 'slug', 'user', 'is_author']
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
return super().update(instance, validated_data)
The print(data) statement from PostFormView.update(self, request, *args, **kwargs) (after manipulation) returns this:
<QueryDict: {'id': ['8'], ..., 'images': [[{'image': <InMemoryUploadedFile: bmw_3.jpeg (image/jpeg)>, 'object_id': 8, 'content_type': 20}, {'image': <InMemoryUploadedFile: bmw_2.jpeg (image/jpeg)>, 'object_id': 8, 'content_type': 20}, {'image': <InMemoryUploadedFile: bmw_1.jpeg (image/jpeg)>, 'object_id': 8, 'content_type': 20}]]}>
Server returns 400_BAD_REQUEST because images were not passed to PostModelSerializer
{"images":["This field is required."]}
i've been facing this issue for 3 days and i can't wrap my head around the root cause.
Thank you for your help.
i have been looking all over the internet but i could not find any anwsers so i had to go this way
I have removed the processing part from PostFormView.update(...) and accessed the images directly in the create & update methods of the ModelSerializer. I'll figure out later on how to handle removing these images
Here's the code:
class PostModelSerializer(WritableNestedModelSerializer):
is_active = serializers.BooleanField(default=True)
path = serializers.HyperlinkedIdentityField(
view_name="api:post-detail", lookup_field='slug')
images = MediaModelSerializer(read_only=True, many=True)
details = DetailModelSerializer(required=False, many=True)
# annotated fields
is_author = serializers.BooleanField(read_only=True, default=False)
class Meta:
model = Post
fields = '__all__'
read_only_fields = ['is_locked', 'slug', 'user', 'is_author']
def create(self, validated_data):
instance = super().create(validated_data)
request = self.context.get('request', None)
if request:
try:
images = request.data.getlist('images')
for image in images:
self.instance.images.create(image=image)
except Exception as e:
pass
return instance
def update(self, instance, validated_data):
instance = super().update(instance, validated_data)
request = self.context.get('request', None)
if request:
try:
images = request.data.getlist('images')
for image in images:
self.instance.images.create(image=image)
except Exception as e:
pass
return instance
If anyone has faced this issue before and managed to resolve it, please post your answer below.
Thank you !

GAE Datastore query

I'm trying to list all the Users that are entered into my datastore in my test GAE app and print out the usernames of each one. However, when I run the code below I get an error saying
self.query = User.all()
AttributeError: type object 'User' has no attribute 'all'
I thought that self.query = User.all() would return all the users in my database?
My code is below.
Thanks in advance!
PS. I know my userfinder form is asking for a username and doing nothing with it, but filtering is a task for later - I just want to make sure I've got the basic query going first.
from google.appengine.ext import ndb
import webapp2
import uuid
class User(ndb.Model):
db_UID = ndb.StringProperty(indexed = True)
db_username = ndb.StringProperty(indexed = True)
db_password = ndb.StringProperty(indexed = True)
db_email = ndb.StringProperty(indexed = True)
db_resetID = ndb.StringProperty(indexed = True)
class UsersPage(webapp2.RequestHandler):
def get(self):
self.response.write('<html><body><h1>User Info Page</h1>')
self.response.write("""
<form method = "post">
Username: <input type = "textarea" name = "user_username"></input><br>
Password: <input type = "textarea" name = "user_password"></input><br>
Email address: <input type = "textarea" name = "user_email"></input><br>
<input type = "submit"></input>
</form>""")
self.response.write('</body></html>')
def post(self):
UNIQUE_ID_STRING = str(uuid.uuid1())
self.user = User(db_UID = UNIQUE_ID_STRING ,
db_username = self.request.get('user_username'),
db_password = self.request.get('user_password'),
db_email = self.request.get('user_email'))
self.user.put()
self.redirect('/user')
class UserFinder(webapp2.RequestHandler):
def get(self):
self.response.write('<html><body><h1>Search username</h1>')
self.response.write("""
<form method = "post">
Username: <input type = "textarea" name = "user_username"></input><br>
<input type = "submit"></input>
</form>""")
self.response.write('</body></html>')
def post(self):
self.query = User.all()
self.response.write('<html><body><h1>Search username</h1>')
for self.user in self.query:
self.response.write('<p>%s</p>' % self.User.db_username)
self.response.write('</body></html>')
application = webapp2.WSGIApplication([
('/user', UsersPage),
('/userfinder', UserFinder),
], debug = True)
Unlike the basic 'db' version, NDB Models don't use all() for querying, they use query(), so
self.query = User.query()
The NDB docs on queries might be useful.
As an aside, using self as much as you are is a little unusual; aside from the calls to self.response.write and to self.redirect, just using local variables will be a lot less confusing (good if you have to post more questions!), and will save you typing.
So your post method would become:
query = User.all()
self.response.write('<html><body><h1>Search username</h1>')
for user in query:
self.response.write('<p>%s</p>' % user.db_username)
self.response.write('</body></html>')

Django model inserting foreign key from same model

I am building a simple comment app in Django. The app allows replies to comments and uses the same model to store comments and replies. My issues is when I try to insert a new reply, the parentpost(FK to parent comment) inserts as NULL. When I use the admin interface to insert a reply, it properly stores the parentpost ID for the parentpost I choose. So I know the issue is not within my model but within my view.
/MODEL/
class UserPost(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True,
help_text='Unique value for product page URL, created from name.', editable = False)
post = models.TextField()
is_active = models.BooleanField(default=True)
meta_keywords = models.CharField("Meta Keywords", max_length=255, blank = True, null = True,
help_text='Content for description meta tag')
meta_description = models.CharField(max_length = 255, blank = True, null = True,
help_text = 'Content for description meta tag')
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
parentpost = models.ForeignKey('self', blank = True, null = True)
class Meta:
#app_label = ''
db_table = 'userposts'
ordering = ['created_at']
verbose_name_plural = 'UserPosts'
def __unicode__(self):
return self.name
#models.permalink
def get_absolute_url(self):
return ('lync_posts', (), {'posts_slug': self.slug})
def save(self):
if not self.id:
d = datetime.datetime.now()
s = d.strftime('%Y-%M-%d-%H-%M-%S-%f')
slugfield = str(self.name + s)
self.slug = slugfield
super(UserPost, self).save()
/VIEW/
def reply(request, slugIn):
parentpostIn = UserPost.objects.get(slug = slugIn)
pid = parentpostIn.id
template_name = 'reply.html'
if request.method == 'POST':
form = forms.ReplyPostForm(data = request.POST)
# create a new item
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
if form.is_valid():
nameIn = form.cleaned_data['name']
postIn = form.cleaned_data['post']
newPost = UserPost(name = nameIn, post = postIn, parentpost = pid)
newPost.save()
return render_to_response(template_name, locals(), context_instance = RequestContext(request))
else:
# This the the first page load, display a blank form
form = forms.NewPostForm()
return render_to_response(template_name, locals(), context_instance=RequestContext(request))
return render_to_response(template_name, locals(), context_instance=RequestContext(request))
You are trying to set the parentpost ForeignKey by id.
You should either use:
newPost = UserPost(name = nameIn, post = postIn, parentpost = parentpostIn)
or (see Django: Set foreign key using integer?):
newPost = UserPost(name = nameIn, post = postIn)
newPost.parentpost_id = pid

Resources