I need to build a rest api using django. On GET requests my tool has to captures parameters from url and invoke a function for their manipulation.
For example: on input url myapp/name=john&birthdate=11July, the function compute(name,birthdate) computes a transformation on parameters returning a json as output. I don't understand how to proceed, considering that each tutorial i followed is about db interaction.
urls.py:
url(r'^myapp/$', views.myapp, name='myapp'),
views.py
def myapp(request):
name = request.GET.get('name', None)
birthdate = request.GET.get('birthdate', None)
if name and birthdate:
result = compute(name, birthdate)
return result
return None
You don't need a database for that. Although you need a database for Django to work.
Related
Say I have two servers prod and API.
On prod I have a Django application running and say I have the following model
class MyModel(model.Models):
name = models.Charfield()
age = models.IntField()
birthdate = models.DateTimeField()
has_birthday = models.BooleanField()
thus the model in my database would look like
name | age | birthdate | has_birthday
-----+-------+-------------+-------------
john 30 1990-01-30 0
doe 20 1987-05-01 0
....
Each day on API I run a daily script which checks, if someone has birthday - if they do, set has_birthday=1 (note, everything above is just an example for illustration purposes).
Since API just is a server for daily jobs, I have not deployed a Django-application on that server, thus I wonder, what is the usual/best way to update the MyModel table with the following logic?
My first intuition is just to make a plain and simple update- SQL statement e.g
from utils import get_con_to_db
con = get_con_to_db()
query = <SQL update query>
con.execute(query)
but that is rather error prone in case I decide to change my model in some way. Is there a better/more secure way of doing so, without having to create a Django application on API thus maintaining two Django applications?
You can define an url with view function do_orm_operation on prod server, then you can execute ORM operation on API server by send a POST request to the url,
its specific implementation is as follows(core idea is to use eval,):
from django.shortcuts import HttpResponse
import django
# Create your views here.
models = django.apps.apps.get_models()
models_names = {mod.__name__: mod for mod in models}
def do_orm_operation(request):
"""
This method supports CURD, you just need to send a request by `POST` method,
the post data should be the format `{'model': 'your_model_name', 'cmd': 'your_orm_operation_begins_with_objects'}`,
Eg: requests.post(data={'model': 'Book', 'cmd': 'objects.filter(name='book1').update(name="mybook")'}, url='http://127.0.0.1:8000/do_orm_operation/')
"""
try:
data = request.POST
model_name = data['model']
cmd = data['cmd']
model = models_names[model_name] # this variable will be used in eval
query = 'model.' + cmd
res = eval(query)
print('res:', res)
# then do whatever you want
return HttpResponse('success')
except:
return HttpResponse('error')
Note:If you use this method, you must pay attention to security issues to prevent malicious execution of database operations.
I want to write a unittest for this method in Django.
def activate(request):
id = int(request.GET.get('id'))
user = User.objects.get(id=id)
user.is_active = True
user.save()
return render(request, 'user_authentication/activation.html')
I wrote sth like this:
def test_activate_view(self):
response = self.client.get('/activation', follow=True)
self.assertTemplateUsed(response, 'user_authentication/activation.html')
It doesn't work because I get an error:
id = int(request.GET.get('id'))
TypeError: int() argument must be a string or a number, not 'NoneType':
What should I change in my test ?
Your view reads data from request.GET - you need to pass this data:
response = self.client.get('/activation?id=1', follow=True)
You also fetch this user afterwards from your database. Therefor you need to load some fixture data. Create a fixture using manage.py dumpdata and load it in your unit test like that:
class UserTestCase(TestCase):
fixtures = ['fixture.json', 'users']
Read the docs about loading fixtures for detailed explanations.
Note regarding your approach
You should not use the users id for this use case. One can easily guess this id and activate accounts.
Someone might register once with a valid email address, receive your link with the id inside and can afterwards create a bunch of accounts without providing a valid email address.
Instead you might generate a unique and random secret (aka token) and associate this token with the user. Your view should accept those tokens and resolve the user based on it. This way one can no longer easily activate.
For my site for auth I'm using https://flask-httpauth.readthedocs.io/en/latest/ . Now I'm trying to make it that it's using data from database. To do that i created database named Users and created columns named username and password.
To get data from this table after defining its class as model I've made get_user functions which looks like it:
#staticmethod
def get_user():
query = (Users
.select())
user = []
for s in query:
user.append(s)
return user
(I'm not sure if it's correct)
Next I had to modify get_pw function but I also wasn't sure how to modify it so I made it look like it:
#auth.get_password
def get_pw(username):
if username in Users.get_user():
return users.get(Users.get_user())
return None
Now after running the site I get prompt to give login and password but those that I set up in my database doesn't seem to work so there must be a problem with get_pw function. Also I'm using peewee SQL to manage database : http://docs.peewee-orm.com/en/latest/peewee/querying.html
You can get rid of your get_user method since you are issuing a very large select query that fetches all records from user table. The get_pw can be redefined as:
def get_pw(username):
user = Users.get(Users.name == username) #assuming you have a username field in model
return user.password #assuming a password field
Also, its a good practice to define your model class as a singular noun rather than plural. So, its better to call it User rather than Users.
This'll help you get started in no time: http://docs.peewee-orm.com/en/latest/peewee/quickstart.html#quickstart
I am trying to make a query system for my website, i think the best way and the most compact would be to assign search variable using url pattern.
So for example, i want to search objects of model User:
User sends HttpRequest to following url:
https://127.0.0.1/search/q="admin"
Now HttpRequest is also sent to search view, we somehow get q variable data.
def search(request):
for query in User.objects.all():
if q in query: # < We somehow need to get data of 'q'.
return HttpResponse(q)
Since i have admin in User.objects.all(), this should return HttpResponse of 'admin'.
How can this url pattern be made? So i can assign q variable from the url and then send it to system to find it?
I have problems with this URL:
https://127.0.0.1/search/q="admin"
There is no ? in the URL, so there is no query string, it's all part of the "path". Using characters like = and " in there will confuse a lot of things, if it works at all.
Either just do
https://127.0.0.1/search/admin
With an URL pattern like r'^search/(?P<querystring>.+)$', or
https://127.0.0.1/search/?q=admin
In this case the query string will be in request.GET['q']; it's also possible to use Django forms to process query parameters (e.g. for validating them).
You can capture named strings from URLs like this:
urls.py:
urlpatterns = [
url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]
views.py:
def page(request, num="1"):
I am trying to write something elegant where I am not relying on Request object in my code. All the examples are using:
(r'^hello/(?P.*)$', 'foobar.views.hello')
but it doesn't seem like you can post to a URL like that very easily with a form. Is there a way to make that URL respond to ..../hello?name=smith
Absolutely. If your url is mapped to a function, in this case foobar.views.hello, then that function might look like this for a GET request:
def hello(request):
if request.method == "GET":
name_detail = request.GET.get("name", None)
if name_detail:
# got details
else:
# error handling if required.
Data in encoded forms, i.e. POST parameters, is available if you HTTP POST from request.POST.
You can also construct these yourself if you want, say, query parameters on a POST request. Just do this:
PARAMS = dict()
raw_qs = request.META.get('QUERY_STRING', '') # this will be the raw query string
if raw_qs:
for line in raw_qs.split("&"):
key,arg = line.split("=")
PARAMS[key] = arg
And likewise for form-encoded parameters in non POST requests, do this:
FORM_PARAMS = QueryDict(request.raw_post_data)
However, if you're trying to use forms with Django, you should definitely look at django.forms. The whole forms library will just generally make your life easier; I've never written a html form by hand using Django because this part of Django takes all the work out of it. As a quick summary, you do this:
forms.py:
class FooForm(forms.Form):
name = fields.CharField(max_length=200)
# other properties
or even this:
class FooForm(forms.ModelForm):
class Meta:
model = model_name
Then in your request, you can pass a form out to the template:
def pagewithforminit(request):
myform = FooForm()
return render_to_response('sometemplate.html', {'nameintemplate': myform},
context_instance=RequestContext(request))
And in the view that receives it:
def pagepostingto(request):
myform = FooForm(request.POST)
if myform.is_valid(): # check the fields for you:
# do something with results. if a model form, this:
myform.save()
# creates a model for you.
See also model forms. In short, I strongly recommend django.forms.
You can't catch GET parameters in a URL pattern. As you can see in django.core.handlers.base.BaseHandler.get_response, only the part of the URL that ends up in request.path_info is used to resolve an URL:
callback, callback_args, callback_kwargs = resolver.resolve(
request.path_info)
request.path_info does not contain the GET parameters. For handling those, see Ninefingers answer.