Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ. (επεξεργασμένο)

Καλησπέρα, έχω εδώ μία βάση Mongo και με Flask εμφανίζω στον Browser τα περιεχόμενα της.

class Get1(Resource):
    def get(self):
        self.db = pymongo.MongoClient("mongodb://127.0.0.1:27017")["NickDB"]["coll"]
        return dumps(self.db.find())

Ο κώδικας είναι απλός και μικρός. Το πρόβλημα; Αυτή η άτιμη η dumps μου πετάει στο browser output αντίστροφες καθέτους ('\') σε όλα τα σημεία του αρχείου. Δηλαδή, το αρχικό JSON αρχείο έχει αυτό:

{ "_id" : { "$oid" : "5b0847b2e866f42f049b741a" }, "retweeted_status" : { "extended_tweet"

Στον browser το output που βγάζει είναι αυτό:

"[{\"_id\": {\"$oid\": \"5b0847b2e866f42f049b741a\"}, \"retweeted_status\": {\"extended_tweet\"

Γενικά, η μορφή που τα εμφανίζει είναι πολύ άσχημη και θα ήθελα γενικότερα να κάνω κάποιου είδους μορφοποίηση για να εμφανίζονται πιο ωραία τα documents στο collection. Ο φίλος μου που δουλεύει Django το έκανε γράφοντας(και διόρθωσε και το πρόβλημα με τις καθέτους έτσι)

HttpResponse(dumps(res), content_type="application/json", status=200)

Εγώ όμως δε ξέρω πώς κάνω κάτι αντίστοιχο. Any ideas για το πώς μπορώ να εμφανίσω τη DB μου στον Browser(φτιάχνω REST API) χωρίς προβλήματα;

Επεξ/σία από Asevastos
Δημοσ. (επεξεργασμένο)
12 λεπτά πριν, Asevastos είπε

Καλησπέρα, έχω εδώ μία βάση Mongo και με Flask εμφανίζω στον Browser τα περιεχόμενα της.


class Get1(Resource):
    def get(self):
        self.db = pymongo.MongoClient("mongodb://127.0.0.1:27017")["NickDB"]["coll"]
        return dumps(self.db.find())

Ο κώδικας είναι απλώς και μικρός. Το πρόβλημα; Αυτή η άτιμη η dumps μου πετάει στο browser output αντίστροφες καθέτους ('\') σε όλα τα σημεία του αρχείου. Δηλαδή, το αρχικό JSON αρχείο έχει αυτό:


{ "_id" : { "$oid" : "5b0847b2e866f42f049b741a" }, "retweeted_status" : { "extended_tweet"

Στον browser το output που βγάζει είναι αυτό:


"[{\"_id\": {\"$oid\": \"5b0847b2e866f42f049b741a\"}, \"retweeted_status\": {\"extended_tweet\"

Γενικά, η μορφή που τα εμφανίζει είναι πολύ άσχημη και θα ήθελα γενικότερα να κάνω κάποιου είδους μορφοποίηση για να εμφανίζονται πιο ωραία τα documents στο collection. Ο φίλος μου που δουλεύει Django το έκανε γράφοντας(και διόρθωσε και το πρόβλημα με τις καθέτους έτσι)

HttpResponse(dumps(res), content_type="application/json", status=200)

Εγώ όμως δε ξέρω πώς κάνω κάτι αντίστοιχο. Any ideas για το πώς μπορώ να εμφανίσω τη DB μου στον Browser(φτιάχνω REST API) χωρίς προβλήματα;

Γιατι θες να δειξεις raw json στον browser? Εκτος απο καποιες πολυ συγκεκριμενες περιπτωσεις δεν θα χρειαστει ποτε να κανεις κατι τετοιο.

Το  backslash το βγαζει για να κανει escape τα ““.

Επισης γιατι σου επιστρεφει $oid μεσα στο _id; 

Επεξ/σία από Predatorkill
Δημοσ. (επεξεργασμένο)

Βασικά, με ενδιαφέρει να δείχνει τα contents της βάσης (τα οποία είναι imported json) στον browser. Υποτίθεται ότι είναι tweets και όταν βάζω στο browser το endpoint /tweets πρέπει να μου τα εμφανίζει. Είχα καταφέρει να τα εμφανίζω όμορφα, διαχωρισμένα σε μορφή MongoDB(εγγραφές η μία κάτω από την άλλη) με αυτόν τον κώδικα:

    def get(self):
        db = client.NickDB
        output = []
        str = db.coll.find
        num = 1
        for q in str():
            output.append('{tweet_%d'%num)
            num+=1
            output.append({ola_ta_fields_tou_kathe_tweet})
            
        return jsonify(output)

εδώ το πρόβλημα είναι άλλο. Ενώ εμφανίζονται όμορφα και ωραία, εάν υπάρχουν σε μία εγγραφή fields χωρίς τιμή, πετάει error. Όπως στη παρακάτω εικόνα στη 2η στήλη υπάρχουν πεδία retweeted_status χωρίς value. Αν πάω μέσα στην output.append να τα περάσω, πετάει κατευθείαν error ο browser.

Πώς μπορώ να το διορθώσω αυτό με το '\' ξέρεις;

Αυτό με το $oid δε το ξέρω, στην δεύτερη μορφή που σου αναφέρω δεν έχω τέτοιο θέμα(2η φωτό).

2i.PNG

 

 

2η εικόνα
sec2.PNG

Επεξ/σία από Asevastos
Δημοσ.

First things first: Όταν ζητάς βοήθεια, να ποστάρεις ολόκληρο τον κώδικα μέσα σε code tags ή σε κάποιο gist. Όταν παίρνεις μήνυμα λάθους να ποστάρεις όλο το Traceback.

Από εκεί και πέρα, ο κώδικας που πόσταρες έχει διάφορα θεματάκια, τόσο όσον αφορά την python όσο και γενικότερες πρακτικές. Googl-αρε με τη σειρά:

  1. How to enumerate in python
  2. https://stackoverflow.com/questions/9109333/is-it-bad-practice-to-use-a-built-in-function-name-as-an-attribute-or-method-ide
  3. What is connection pooling and why should I use it
  4. Pymongo + Connection pooling

μετά από αυτά, για να έρθουμε στο 1o πρόβλημα σου.

Έχεις ένα web server στον οποίο κάνεις Requests. O web server σου επιστρέφει Responses. Από τι αποτελείται ένα response; Εδώ η απάντηση. Ο client του API σου, ο browser δηλαδή στην περίπτωσή σου, χρησιμοποιεί ένα header από το Response για να αποφασίσει ποιο representation που θα σου δείξει. Ποιο header είναι αυτό; Το ίδιο ακριβώς που έθεσε και ο φίλος σου. Αν διαβάσεις λίγο θα δεις ότι η συνάρτηση jsonify(), που είναι ένα wrapper γύρω από το json.dumps(), θέτει το content-type από μόνη της. Για αυτό και η δεύτερη εκδοχή δουλεύει ενώ η πρώτη όχι.

Για να το καταλάβεις ακόμα καλύτερα, φτιάξε μια 2η μέθοδο που θα επιστρέφει τα ίδια data με την πρώτη αλλά με διαφορετικό content-type για να δεις διαφορές.

Για το δεύτερο πρόβλημά σου, πόσταρε traceback και sample data.

ΥΓ. Άμα έχεις χρόνο, διάβασε και το RFC: https://www.w3.org/Protocols/rfc2616/rfc2616.html

 

  • Thanks 1
Δημοσ. (επεξεργασμένο)

Ευχαριστώ πολύ για το αναλυτικό post. Μου έχει μείνει από το SO λίγους μήνες πριν που έβαλα ολόκληρο τον κώδικα και μου το έκαναν edit γιατί είχε περιττές λεπτομέρειες(imports κ.λπ). Για αυτό και εστίασα τώρα μόνο στο συγκεκριμένο κομμάτι. Εν πάσει περιπτώσει, πριν από τη προαναφερθείσα κλάση υπάρχουν imports και τα εξής δύο για το initialization του Flask:

app = Flask(__name__)
api = Api(app)

Είδα τις 4 αναφορές που πρότεινες, το 2ο είναι pretty clear. Άθελα, μπορεί να χρησιμοποίησα κάποιο name που καλό είναι να μη χρησιμοποιείται.
Για το 1, αναφέρεσαι στον counter που έβαλα στη for loop;
Για τα 3-4, κατάλαβα την έννοια αλλά δε καταλαβαίνω το λάθος μου. Η κλήση στη βάση γίνεται μία φορά συνολικά, δεν είναι ότι καλώ δύο φορές ξεχωριστά επειδή έχω 2 διαφορετικούς τρόπους κλήσης. 

Κατάλαβα και το κομμάτι με τα requests, ευχαριστώ. Αν και τα είχα δει ήδη πριν ξεκινήσω αλλά πολύ επιφανειακά.

Τώρα, για το δεύτερο πρόβλημα, ο κώδικας είναι ο εξής:

from bson import json_util
from flask import Flask, request
from flask_restful import Resource, Api
from pymongo import MongoClient
import pymongo
import simplejson as json
from bson.json_util import dumps
from flask import jsonify
from pprint import pprint
import requests

app = Flask(__name__)
api = Api(app)

class Get1(Resource):
    def get(self):
        db = client.NickDB
        output = []
        str = db.coll.find
        num = 1
        for q in str():
            output.append('{tweet_%d'%num)
            num+=1
            output.append({'_id':q['id'],'id_str': q['id_str'],'truncated':q['truncated'],'in_reply_to_status_id':q['in_reply_to_status_id'],'quote_count':q['quote_count'],'retweeted':q['retweeted'],'retweet_count':q['retweet_count'],'timpestamp_ms':q['timestamp_ms'],'favorite_count':q['favorite_count'],'created_at':q['created_at'],'coordinates':q['coordinates'],'in_reply_to_user_id':q['in_reply_to_user_id'],'favorited':q['favorited'],'entities':q['entities'],'in_reply_to_screen_name':q['in_reply_to_screen_name'],'id':q['id'],'contributors':q['contributors'],'user':q['user'],'source':q['source'],'is_quote_status':q['is_quote_status'],'in_reply_to_status_id_str':q['in_reply_to_status_id_str'],'reply_count':q['reply_count'],'filter_level':q['filter_level'],'place':q['place'],'in_reply_to_user_id_str':q['in_reply_to_user_id_str'],'geo':q['geo'],'text':q['text'],'lang':q['lang']})
        return jsonify(output)
      
api.add_resource(Get1, '/tweets')

if __name__ == '__main__':
    app.run(debug=True)

Τέσσερα πράγματα:

  • Τα imports είναι πολλά γιατί δοκίμασα πολλά πράγματα και δε τα "καθάρισα" ακόμη.
  • Κατάφερα να το λύσω, πάλι με append αλλά αυτή τη φορά αντί να περνάω ένα-ένα τα fields απλώς πέρασα ως όρισμα το documents (όπου documents=db.find()).
  • Ο τρόπος που εισάγω στην output τα δεδομένα είναι τρομερά μπακάλικος, I know. Αλλά το tutorial που κοιτούσα έτσι το έκανε(μόνο που αυτός είχε να εμφανίσει 2 fields, όχι καμιά 25αριά).
  • Για το traceback, παραθέτω τι μου εμφανίζει εάν στο κώδικα μου γράψω 
    output.append({...,'retweet_status':q['retweet_status'],...})
    Spoiler
    
    Traceback (most recent call last):
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 2309, in __call__
        return self.wsgi_app(environ, start_response)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 2295, in wsgi_app
        response = self.handle_exception(e)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask_restful\__init__.py", line 269, in error_router
        return original_handler(e)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 1741, in handle_exception
        reraise(exc_type, exc_value, tb)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\_compat.py", line 34, in reraise
        raise value.with_traceback(tb)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 2292, in wsgi_app
        response = self.full_dispatch_request()
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request
        rv = self.handle_user_exception(e)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask_restful\__init__.py", line 269, in error_router
        return original_handler(e)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 1718, in handle_user_exception
        reraise(exc_type, exc_value, tb)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\_compat.py", line 34, in reraise
        raise value.with_traceback(tb)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
        rv = self.dispatch_request()
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\app.py", line 1799, in dispatch_request
        return self.view_functions[rule.endpoint](**req.view_args)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask_restful\__init__.py", line 458, in wrapper
        resp = resource(*args, **kwargs)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask\views.py", line 88, in view
        return self.dispatch_request(*args, **kwargs)
      File "C:\Users\Nick\PycharmProjects\untitled4\venv\newenv\newempty\lib\site-packages\flask_restful\__init__.py", line 573, in dispatch_request
        resp = meth(*args, **kwargs)
      File "C:\Users\Nick\PycharmProjects\untitled4\Projectia.py", line 35, in get
        output.append({'_id':q['retweet_status'],'id_str': q['id_str'],'truncated':q['truncated'],'in_reply_to_status_id':q['in_reply_to_status_id'],'quote_count':q['quote_count'],'retweeted':q['retweeted'],'retweet_count':q['retweet_count'],'timpestamp_ms':q['timestamp_ms'],'favorite_count':q['favorite_count'],'created_at':q['created_at'],'coordinates':q['coordinates'],'in_reply_to_user_id':q['in_reply_to_user_id'],'favorited':q['favorited'],'entities':q['entities'],'in_reply_to_screen_name':q['in_reply_to_screen_name'],'id':q['id'],'contributors':q['contributors'],'user':q['user'],'source':q['source'],'is_quote_status':q['is_quote_status'],'in_reply_to_status_id_str':q['in_reply_to_status_id_str'],'reply_count':q['reply_count'],'filter_level':q['filter_level'],'place':q['place'],'in_reply_to_user_id_str':q['in_reply_to_user_id_str'],'geo':q['geo'],'text':q['text'],'lang':q['lang']})
    KeyError: 'retweet_status'

     

Επεξ/σία από Asevastos
Δημοσ. (επεξεργασμένο)

how to ask good questions 101

 Πρέπει να μάθεις να φτιάχνεις ένα Minimal Working Example (mwe). Η έμφαση είναι εξίσου στο minimal όσο και στο working. Για το minimal, όση περισσότερη εμπειρία αποκτάς τόσο πιο εύκολο θα είναι να καταλαβαίνεις τι αποτελεί χρήσιμο context και τι όχι. Αν δεν είσαι σίγουρος, καλύτερα περισσότερο context παρά λιγότερο. Παρόλα αυτά είναι good manners να δείξεις ότι έκανες προσπάθεια να το κρατήσεις minimal και ότι δεν αντέγραψες απλά ό,τι είχες στον editor σου.

https://en.wikipedia.org/wiki/Minimal_working_example

https://stackoverflow.com/help/mcve

Το παράδειγμα που έβαλες στο προηγούμενο post δεν είναι ούτε minimal, αλλά ούτε και working. Που ορίζεται πχ το "client"? Μόλις παει ο κώδικας στο db = client.NickDB θα πετάξει NameError

1. ναι για το counter λεω. H function που θες είναι η enumerate()

2. 99% όταν βλέπω str() σκέφτομαι ένα κενό string και όχι το wrapper object του cursor της mongo.

3-4. Αυτό ξεφεύγει λίγο από το πλαίσιο της άσκησης σου, αλλά στο ανέφερα γιατί είναι bad practice και γιατί φαίνεσαι να ψάχνεσαι λίγο. H απάντηση είναι στο FAQ της pymongo. Εσύ φτιάχνεις ένα νεό instance της pymongo.MongoClient() αντί να χρησιμοποιεις το ίδιο. Σκέψου πως θα το κάνεις να ξαναχρησιμοποιείται το ίδιο instance. Για να ελέγξεις αν είναι το ίδιο instance ή όχι μπορείς να χρησιμοποιήσεις την συνάρτηση id(). Πχ

client1 = pymongo.MongoClient(...)
client2 = pymongo.MongoClient(...)
print(id(client1))
print(id(client2))

Tα Requests/Responses και γενικότερα το HTTP κοίτα να τα καταλάβεις καλά. Αν κάτι έχεις να πάρεις από τις ασκήσεις αυτές, αυτό είναι. Τα υπόλοιπα (flask, python κτλ) είναι implementation details (χωρίς να σημαίνει ότι δεν έχουν την αξία τους).

41 λεπτά πριν, Asevastos είπε

output.append('{tweet_%d'%num)

αυτή η αγκύλη τι είναι;

edit

Για να καταλάβεις γιατί παίρνεις KeyError δώσε ένα print(q.keys()) πριν το δεύτερο append()

 

Επεξ/σία από pmav99
Δημοσ.

Understood το μήνυμα για το mwe. :) 

Για την enumerate έχεις δίκιο απλώς εκείνη τη στιγμή εφάρμοζα ότι ήξερα από τις άλλες γλώσσες για να έχω τον counter. Για το όνομα συμφωνώ ότι ήταν φάουλ το str.

Γενικά, όπως ίσως καταλαβαίνεις, απλώς θέλω να τελειώνω με την εργασία για να περάσω το μάθημα. Ωστόσο, συμφωνώ ότι η γνώση για τα requests (πέραν των πλαισίων της εργασίας) είναι MUST και ήταν καλή αφορμή η εργασία για να δουλέψω λίγο με αυτά στη πράξη (και να καταλάβω επιτέλους τι είναι το API). Η αγκύλη αφορά το συγκεκριμένο format με το οποίο πρέπει να εμφανίζονται τα data μου στο browser ;)

 

Δημοσ.
3 ώρες πριν, Asevastos είπε

Η αγκύλη αφορά το συγκεκριμένο format με το οποίο πρέπει να εμφανίζονται τα data μου στο browser

Δεν θα πρέπει να κλείνει;

Υπόψιν, ενα ζεύγος από αγκύλες σε string έχει ειδικό νόημα στην python. θα χρειαστεί να το κάνεις escape.

Δημοσ.

Θα πρέπει να κλείνει στο τέλος των tweets.

Αλήθεια; Δε ξέρω Oython, αναγκαστικά τη χρησιμοποιώ για την εργασία. Ευχαριστώ για τη διευκρίνιση. :D

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...