Many questions has been asked on Stack Overflow for RESTful URL design
To name a few...
Hierarchical URL Design:
Hierarchical RESTful URL design
Understanding REST: Verbs, error codes, and authentication: Understanding REST: Verbs, error codes, and authentication
So I am well aware for Restful URL Design.
However, how about URL Design for the Browser for traditional Websites which are not Single Page Applications (SPA).
For the purpose of this example, lets assume we have a Book Database. Lets further assume we have 2 traditional HTML sites created.
HTML Table for showing all books
HTML Form for showing one book (blank or pre-filled with book details)
Now we want that the user of our website can do CRUD operations with it. How about the following URL Design then:
GET /book/show/all // HTML Table
GET /book/show/{id} // HTML Form pre-filled
GET /book/new // HTML Form blank
POST /book/new // Submit HTML Form
POST /book/update/{id} // Submit updated HTML Form
POST /book/delete/{id} // A Link/Button with POST ability (no JS needed)
Questions:
Best practise Browser URL Design
Am I following best practice for URL Design in the Browser (I am not talking about REST here)? Also regarding SEO, Bookmarking and short URL Design? I was thinking of something like: /resource/action/ ...
GET and POST only URL Design
Browsers can only make GET and POST unless someone uses JavaScript. Considering the above URL Design, should it be wiser to introduce JavaScript and make PUT and DELETE requests for updating and deleting a resource? Or should I stay with GET and POST only?
Cheers
Instead of CRUD (create-read-update-delete), I prefer the acronym (D)AREL (display, add, remove, edit, list) -- the (D) is silent ;-)
While not all RESTful API design choices make sense for a browser based crud app, we can borrow much of it, e.g.:
GET /books -- html table listing all books (alternatively /books/list to go with the DAREL acronym)
GET /books/add -- display a form for adding a new book
POST /books/add -- adds a new book and redirects to /book/1 (where 1 is a new book id)
I personally prefer to use plural nouns for collections and singular nouns for items, so..
GET /book/1 -- display book 1 info (e.g. a customer view)
GET /book/1/edit -- display a form to edit /book/1
POST /book/1/edit -- updates /book/1 and redirects to /book/1
GET /book/1/remove -- maybe/probably optional
POST /book/1/remove -- normally /book/1/edit will have a delete button that handles "are you sure..?" and posts here, redirects to /books
The uri scheme is /resource/unique-identifier/action. The (D) / display action is silent/default for a given resource uri.
This also works if you want to model that a book can have multiple authors:
GET /book/1/authors -- list all authors for /book/1
GET /book/1/authors/add -- add author form
GET /book/1/author/1
GET /book/1/author/1/edit
// etc.
although you will likely need a separate/additional url-hierarchy for authors:
GET /authors
GET /authors/add
GET /author/1
// etc.
and similarly, books that an author has written:
GET /author/1/books
// etc.
Most modern web-apps use ajax calls for sub-resources though, so here you can use a pure RESTful api too:
GET /api/book/1/authors -- returns list of all authors for /book/1
POST /api/book/1/authors -- create a new author, returns the new author uri, e.g. /api/author/1
GET /api/author/1 -- get /author/1 info according to MIME type etc.
PUT /api/author/1 -- update /author/1
DELETE /api/author/1 -- delete the /author/1 resource
DELETE /api/book/1/author/1 -- delete author/1 from /book/1? (or maybe this is covered by PUT /api/author/1 ?)
The translation from the original url-scheme is pretty mechanical
/resource/unique-id/action -> http-verb /resource/unique-id
where action = http-verb
display = GET (on a singular resource)
add = POST
remove = DELETE
edit = PUT
list = GET (on a plural/collection resource)
Related
We are thinking about migration from Kentico CMS to Kontent.
So far we are found answers to most of our navigation related questions. But one question remains not clear. How to implement page aliases in MVC.NET+Kontent application.
Not rare business case when some page, let's say sub-productAA is being moved from productA to ProductB.
For instance: from /products/ProductA/sub-productAA it becomes /products/ProductB/sub-productsAA .
Let's assume, that sub-productAA was advertised in a 3rd party website by it's original URL: /products/ProductA/sub-productAA .
Now original URL is dead.
How to handle this situation?
Another example that product has been renamed and slug has been renamed as well to better address product mean. And original URL has been published elsewhere.
How handle it and properly redirect to a new URL ?
Thank you,
Kontent doesn't provide you with any built-in means to handle page aliases. As a headless CMS, it is up to you to decide how you map content items to URLs.
I don't think there is any single correct solution for this. If you don't have a huge number of aliases to manage you could handle them within Kontent by creating a content type that has a slug and a linked item so you can use this to store aliases and redirect to the correct content item. For your example:
product content type item has a slug of "ProductB/sub-productsAA"
alias content type item has a slug of "ProductA/sub-productAA"
you then have an action that retrieves content by slug from products:
var response = await _deliveryClient.GetItemsAsync<Product>(new EqualsFilter($"elements.{Product.SlugCodename}", productUrlSlug));
and if none is returned do another search against aliases:
var response = await _deliveryClient.GetItemsAsync<ProductAlias>(new EqualsFilter($"elements.{ProductAlias.SlugCodename}", productUrlSlug));
That way you only make the second call if the first fails. You can then retrieve the slug for the linked item and redirect the client to the correct URL.
The downside approach is that it requires content editors to manage aliases. Not a bad thing, as they should be thinking about the consequences of changing URLs.
An alternative approach would be to track aliases on your server. You could use webhooks to receive a notification about content changes. If a product slug has been modified you could store the old value and use custom middleware to redirect to the new version. More work, but no editor effort required.
I want to make sure that my visitors (not authenticated users), are unable to visit a particular view without coming directly from a "previous view". I've kind of had to manually create a form preview and confirmation state. It's the step between submission and preview, and preview and confirm I'd like to "secure".
form submission-view -> preview-view -> confirm-view.
Is there some way that I can create a unique hash, POST it, and check if it's correct, or somewhat generate a cookie, session — or anything else that feels clever?
I'm a Django beginner (programming beginner in general) and any snippets' or pointing me in a right direction would be very much appreciated!
Thanks.
There are at least two ways you can accomplish this that I can think of:
One would be to include a hidden field in your form or querystring value that contains your hash/unique that you want to pick up in the next view. If it's not there, or incorrect, redirect.
Another would be to check the referring url from the request.META to see if they've come from the view you want them to come in on first, and save a session value from the form submission to carry through the rest of the views. If it's not there, redirect. If the referring URL isn't what you expect, redirect.
Whether you use a cookie, session, querystring parameter or hidden form post, it's all doing the same thing - validating a value exists. Whatever method works best, is what makes the most sense for you as the developer and most likely maintainer of said app.
To POST data to the server for an object, one must provide the object's ID for lookup. Where to include the ID and send it to the server is something that has been bugging me. There are three options I have in mind:
Include the ID as part of the URL
Include the ID as part of the POST data
Put the ID in query string (I'm not sure if query string is allowed when doing a POST)
I use python/django for my server end.
Someone please comment on which one I should go with and why. Thanks.
I'd do it via the URL in most cases, particularly if you are creating a view to display an object. Furthermore, I'd use the slug (if there is one) instead of an ID as it looks better in Google SERP, it makes more sense semantically, and it is more readable for users.
Remember that you can easily reverse object's urls using get_absolute_url(), the {% url ... %} tag and the reverse() function. You won't be able to avail of these if you use query strings or Posts to display your objects.
Regarding query strings/parameters; I usually go by the rule of constructing the queryset with my URL, but filtering it with parameters. i.e. If I want to see all posts tagged with something, I get the main queryset via the URL /posts/tagged/some_tag and then drill them down where necessary with parameters; /posts/tagged/some_tag?rating=2
Regarding POST data; this should only really apply when you are presenting a form to edit an object. I would still use the URL to find and display the form (i.e. /posts/my-post-slug/edit/) and I would use parameters to control any options or features (i.e. /posts/my-post/slug/edit/?highlight_required_fields=true) but all the fields of the form would be submitted via POST
I'm going to be honest: this is a question I asked on the Django-Users mailinglist last week. Since I didn't get any replies there yet, I'm reposting it on Stack Overflow in the hope that it gets more attention here.
I want to create an app that makes it easy to do user friendly,
multiple / mass file upload in your own apps. With user friendly I
mean upload like Gmail, Flickr, ... where the user can select multiple
files at once in the browse file dialog. The files are then uploaded
sequentially or in parallel and a nice overview of the selected files
is shown on the page with a progress bar next to them. A 'Cancel'
upload button is also a possible option.
All that niceness is usually solved by using a Flash object. Complete
solutions are out there for the client side, like: SWFUpload
http://swfupload.org/ , FancyUpload http://digitarald.de/project/fancyupload/
, YUI 2 Uploader http://developer.yahoo.com/yui/uploader/ and probably
many more.
Ofcourse the trick is getting those solutions integrated in your
project. Especially in a framework like Django, double so if you want
it to be reusable.
So, I have a few ideas, but I'm neither an expert on Django nor on
Flash based upload solutions. I'll share my ideas here in the hope of
getting some feedback from more knowledgeable and experienced people.
(Or even just some 'I want this too!' replies :) )
You will notice that I make a few assumptions: this is to keep the
(initial) scope of the application under control. These assumptions
are of course debatable:
All right, my idea's so far:
If you want to mass upload multiple files, you are going to have a
model to contain each file in. I.e. the model will contain one
FileField or one ImageField.
Models with multiple (but ofcourse finite) amount of FileFields/
ImageFields are not in need of easy mass uploading imho: if you have a
model with 100 FileFields you are doing something wrong :)
Examples where you would want my envisioned kind of mass upload:
An app that has just one model 'Brochure' with a file field, a
title field (dynamically created from the filename) and a date_added
field.
A photo gallery app with models 'Gallery' and 'Photo'. You pick a
Gallery to add pictures to, upload the pictures and new Photo objects
are created and foreign keys set to the chosen Gallery.
It would be nice to be able to configure or extend the app for your
favorite Flash upload solution. We can pick one of the three above as
a default, but implement the app so that people can easily add
additional implementations (kinda like Django can use multiple
databases). Let it be agnostic to any particular client side solution.
If we need to pick one to start with, maybe pick the one with the
smallest footprint? (smallest download of client side stuff)
The Flash based solutions asynchronously (and either sequentially or
in parallel) POST the files to a url. I suggest that url to be local
to our generic app (so it's the same for every app where you use our
app in). That url will go to a view provided by our generic app.
The view will do the following: create a new model instance, add the
file, OPTIONALLY DO EXTRA STUFF and save the instance.
DO EXTRA STUFF is code that the app that uses our app wants to run.
It doesn't have to provide any extra code, if the model has just a
FileField/ImageField the standard view code will do the job.
But most app will want to do extra stuff I think, like filling in
the other fields: title, date_added, foreignkeys, manytomany, ...
I have not yet thought about a mechanism for DO EXTRA STUFF. Just
wrapping the generic app view came to mind, but that is not developer
friendly, since you would have to write your own url pattern and your
own view. Then you have to tell the Flash solutions to use a new url
etc...
I think something like signals could be used here?
Forms/Admin: I'm still very sketchy on how all this could best be
integrated in the Admin or generic Django forms/widgets/...
(and this is were my lack of Django experience shows):
In the case of the Gallery/Photo app:
You could provide a mass Photo upload widget on the Gallery detail
form. But what if the Gallery instance is not saved yet? The file
upload view won't be able to set the foreignkeys on the Photo
instances. I see that the auth app, when you create a user, first asks
for username and password and only then provides you with a bigger
form to fill in emailadres, pick roles etc. We could do something like
that.
In the case of an app with just one model:
How do you provide a form in the Django admin to do your mass
upload? You can't do it with the detail form of your model, that's
just for one model instance.
There's probably dozens more questions that need to be answered before
I can even start on this app. So please tell me what you think! Give
me input! What do you like? What not? What would you do different? Is
this idea solid? Where is it not?
Thank you!
I just released a simple app for this about a month ago: django-uploadify.
It's basically a Django template tag that acts as a wrapper for the very nifty Uploadify (requires jQuery). Using it is as simple as adding this to your template...
{% load uploadify_tags }{% multi_file_upload ‘/upload/complete/url/’ %}
The tag will fire events (1 per file) on both the client-side and server-side (Django signal) to indicate when an incoming file has been received.
For example, assuming you have a model 'Media' that handles all user-uploaded files...
def upload_received_handler(sender, data, **kwargs):
if file:
new_media = Media.objects.create(
file = data,
new_upload = True,
)
new_media.save()
upload_recieved.connect(upload_received_handler, dispatch_uid=‘whatever.upload_received’)
Check out the wiki for info on how to set it up and create the signal handlers (client/server).
About your conceptual implementation from above, here's a few points of consideration:
Having the app automatically create the "File Model" instance probably isn't as robust as people may already have their own models they're working with
If you want to implement any type of security or authentication, you need an open system and less of an 'auto-create' type
I really think signals/events are the way to handle this, and also handle the 'DO OTHER STUFF' part of what you mentioned.
My conclusion was that multi-upload can never really be a form widget in the sense that Django implements form widgets. 1 file will most likely be represented by 1 model instance (with some exceptions), which means that we end up with a situation where 1 widget can represent N model instances. However Django is setup so that a widget represents 1 value for 1 field in 1 instance. It just doesn't fit for the majority of use-cases to have it as a widget (hence why I went the template tag route).
I am building a web app that allows our field staff to create appointments. This involves creating a record that contains many foreign keys, of which some come from very large tables. For example, the staff will need to select one of potentially thousands of customers.
What's the best way of doing this in Django?
A pop-up box that allows the users to search for customers, gives them the results, the user selects the results, then fills out the main appointment form and then
disappears?
Changing the appointments form to a customer selection page that
then reloads the appointments page with the data in a hidden form? Or
holding the data in some session variables?
Some from of Ajax approach.
A wizard where the flow is: a customer search page, a list of results and they select from results, then a search page for the next option (for example product selection), etc etc
(I'd like to keep it as simple as possible. This is my first Django
project and my first web project for more years than I care to
remember)
ALJ
Imho you should consider some kind of autocomplete fields. I think this results in the best usability for the user. Unfortunately, this always involves Ajax. But if you think that all users have JS turned on this is no problem.
E.g.
django-autocomplete
or what is probably more powerful:
django-ajax-selects
If you do the wizard approach, it will take longer for the user to accomplish the task and makes it harder to change selections.
Edit:
Well with django-ajax-selects you can define how the results should look like. So you can e.g. add the address behind the name.
Quote:
Custom search channels can be written when you need to do a more complex search, check the user's permissions, format the results differently or customize the sort order of the results.
I have done this before by integrating a jQuery autocomplete plugin. But, seeing as this is your first project and your desire to keep it simple, I suppose you could go with the session data option. For instance, you could show a search page where users could search for and select a customer. You could then store the, say, ID of the selected customer object as session data, and use it to pre-populate the corresponding field in the form when displaying the form. That's what I think offhand.