Creating unique URL/address for a resource to share - Best practices - django

In my application there is a need to create unique URLs (one per resource) that can be shared. Something like Google Calendar Private address for a calendar. I want to know what are the best practices for this.
If it helps my application is in Django.
Please let me know if this question needs more explanation.

This should be very straightforward. In your urls.py file you want a url like this:
url(r'/resource/(?P<resource_name>\w+)', 'app.views.resource_func', name="priv-resource"),
Then you handle this in views.py with a function called:
def resource_func(request, resource_name):
# look up resource based on unique string resource_name...
Finally, you get to use this in your templates too, using naming:
{% url priv-resource string %}
Just ensure that in your models.py:
class ResourceModel(models.Model)
resource_name = models.CharField(max_size=somelimit, unique=True)
I might even be tempted to use a signal handler to generate this field automatically upon save of the object. See the documentation.

Related

How do I reverse the URL for an admin action?

I'm posting this because I searched stackoverflow and docs for a long time without finding an answer -- hopefully this helps somebody out.
The question is, for testing purposes, how do I find the URL that's related to admin actions for a specific model?
Admin model urls can all be found by reverse(admin:appname_modelname_*), where * is the action (change, delete, etc). But I couldn't find one for the admin actions, and since I was defining custom actions, I'd like to get the url.
This took a fair bit of digging, I couldn't find anything in the Django docs about it and I ended up having to inspect the source code of a third party library.
Essentially there are 2 URL patterns, one for bulk actions and one for object actions:
Bulk: r'admin/<app_label>/<model_name>/actions/(?P<tool>\\w+)/$'
Object: r'admin/<app_label>/<model_name>/(?P<pk>.+)/actions/(?P<tool>\\w+)/$'
The URL name pattern is <app_label>_<model_name>_actions
Therefore we can reverse the bulk view:
Using args: reverse("admin:<app_label>_<model_name>_actions", args=["foo"])
Using kwargs: reverse("admin:<app_label>_<model_name>_actions", kwargs={"tool": "foo"})
and reverse the object view:
Using args: reverse("admin:<app_label>_<model_name>_actions", args=[1, "foo"])
Using kwargs: reverse("admin:<app_label>_<model_name>_actions", kwargs={"pk": 1, "tool": "foo"})
The URL for all custom actions is reverse(admin:<appname>_<modelname>_changelist), but the action name is specified in the action field of the POST data.
The answer, which is hard to find, is that actions are referenced by reverse(admin:appname_modelname_changelist)

Generate url for Django Simple History historical object

Given an model called Stuff, I want the url to a HistoricalStuff object.
In other words, how does one implement get_historical_url in the below code snippet?
stuff = Stuff.objects.first()
stuff.pk
-> 100
historical_stuff = stuff.history.first() # we want to get url for this
historical_stuff.pk
-> 1
get_historical_url(historical_stuff)
-> /path/to/admin/stuff/100/history/1
Obviously the dumb solution would be to use a format string but I'd rather use urlresolvers
After much digging around, I found in the simple history source code that the url name is similar to the admin change names, namely admin_%s_%s_simple_history.
With this knowledge, get_historical_url looks like
def get_simplehistory_url(history_obj):
parent_obj = history_obj.history_object
return urlresolvers.reverse('admin:{}_{}_simple_history'.format(
parent_obj._meta.app_label, parent_obj._meta.model_name), args=(parent_obj.id, history_obj.pk))

Using drf-nested-routers with nested HyperlinkedIdentityFields

I am trying to generate nested HATEOAS links in a serializer using the drf-nested-routes package. My current setup would be as follows:
/resource_a/<pk>
/resource_a/<pk>/resource_b/<pk>
/resource_a/<pk>/resource_b/<pk>
/resource_a/<pk>/resource_b/<pk>/resource_c
I am unable to create a HyperlinkedIdentityField that points to the last route. According to the documentation, one can create hyperlinked fields like this:
nameservers = HyperlinkedIdentityField(
view_name='domain-nameservers-list',
lookup_url_kwarg='domain_pk'
)
Or
nameservers = NestedHyperlinkedRelatedField(
many=True,
read_only=True, # Or add a queryset
view_name='domain-nameservers-detail'
parent_lookup_url_kwargs={'domain_pk': 'domain__pk'}
)
But these approaches fail when trying to reach a resource that is 2 layers deep in the URL hierarchy. The first method is not compatible, as it does not allow to add a second lookup_url_kwarg, and as for the second one, it throws an exception (ImproperlyConfigured) when configuring with the (in my opinion) proper attributes (resource_a__pk, resource_b__pk).
Is this at all possible with this package? Otherwise I will resort to a simpler solution using a SerializerMethodField:
resource_c = serializers.SerializerMethodField()
def get_resource_c(self, obj):
url = reverse('resource_b-resource_c-list', kwargs=dict(resource_a_pk=obj.resource_a.pk, resource_b_pk=obj.pk))
return self.context['request'].build_absolute_uri(url)
Thanks in advance!
I've done this before using NestedHyperlinkedRelatedField and it definitely works. My guess is that your configuration is not correct. One thing I noticed is that you use parent_lookup_url_kwargs while in my case I use parent_lookup_kwargs.
Based on your explanation I think it needs to look like this
NestedHyperlinkedRelatedField(...,
parent_lookup_kwargs={
'resource_a_pk': '<how to reach resource_a pk from resource_b>'})

Django: How to access the model id's within an AJAX script?

I was wondering what is the correct approach,
Do I create HiddenInput fields in my ModelForm and from the
View I pass in the primaryKey for the models I am about to edit into
the hiddenInput fields and then grab those hiddenInput fields from
the AJAX script to use it like this?
item.load(
"/bookmark/save/" + hidden_input_field_1,
null,
function () {
$("#save-form").submit(bookmark_save);
}
);
Or is there is some more clever way of doing it and I have no idea?
Thanks
It depends upon how you want to implement.
The basic idea is to edit 1. you need to get the existing instance, 2. Save provided information into this object.
For #1 you can do it multiple ways, like passing ID or any other primary key like attribute in url like http://myserver/edit_object/1 , Or pass ID as hidden input then you have to do it through templates.
For #2, I think you would already know this. Do something like
inst = MyModel.objects.get(id=input_id) # input_id taken as per #1
myform = MyForm(request.POST, instance=inst)
if myform.is_valid():
saved_inst = myform.save()
I just asked in the django IRC room and it says:
since js isn't processed by the django template engine, this is not
possible.
Hence the id or the object passed in from django view can't be accessed within AJAX script.

Django: Passing data to view from url dispatcher without including the data in the url?

I've got my mind set on dynamically creating URLs in Django, based on names stored in database objects. All of these pages should be handled by the same view, but I would like the database object to be passed to the view as a parameter when it is called. Is that possible?
Here is the code I currently have:
places = models.Place.objects.all()
for place in places:
name = place.name.lower()
urlpatterns += patterns('',
url(r'^'+name +'/$', 'misc.views.home', name='places.'+name)
)
Is it possible to pass extra information to the view, without adding more parameters to the URL? Since the URLs are for the root directory, and I still need 404 pages to show on other values, I can't just use a string parameter. Is the solution to give up on trying to add the URLs to root, or is there another solution?
I suppose I could do a lookup on the name itself, since all URLs have to be unique anyway. Is that the only other option?
I think you can pass a dictionary to the view with additional attributes, like this:
url(r'^'+name +'/$', 'misc.views.home', {'place' : place}, name='places.'+name)
And you can change the view to expect this parameter.
That's generally a bad idea since it will query the database for every request, not only requests relevant to that model. A better idea is to come up with the general url composition and use the same view for all of them. You can then retrieve the relevant place inside the view, which will only hit the database when you reach that specific view.
For example:
urlpatterns += patterns('',
url(r'^places/(?P<name>\w+)/$', 'misc.views.home', name='places.view_place')
)
# views.py
def home(request, name):
place = models.Place.objects.get(name__iexact=name)
# Do more stuff here
I realize this is not what you truly asked for, but should provide you with much less headaches.