I am trying to write a shipping method base on both by country and also weight-base in Django-oscar. It seems the default shipping methods must also have these
from oscar.apps.shipping.methods import Free, FixedPrice, NoShippingRequired
I do not required any of the above and would only provide discount for shipping through discounts.
How do I write by repository.py so I do not apply any of these oscar.apps.shipping.methods import Free, FixedPrice, NoShippingRequired
So I can just write my class Repository (CoreRepository):
Without writing
methods += [NoShippingRequired()]
methods += [FixedPrice()]
methods += [Free()]
The method I have written is not code based but implemented through the shipping menu in the dashboard. I followed the following to set my shipping.
https://groups.google.com/forum/#!topic/django-oscar/H4tf20ujm8k
When testing, on the page 'Shipping Menu', both the 'HandDelivery', and my weight-based by country shipping method button is displayed to customer. Which means customer can click the HandDelivery button too even when customer is based internationally. I wished to disable that 'HandDelivery' button at the Shipping method page, so it is not an option for customers to select at all.
Another option is to attached a message to this button to make it clear to customers that clicking that button means arranging to collect item from warehouse within 1 week of reservation.
How do I display that the message to the customer? Customer is not taking onto the payment page. And an email is sent so items can be collected within 7 days? Like similar to argos, reserve, item, go to shop, pay, and collect. So I could change the discription of 'HandDelivery' to reserve. Then customer does not pay but pay upon collection. But how?
EDIT: Apparently Oscar has several ways to define shipping; updating answer to cover methods defined in the dashboard!
Once you have forked Oscar's shipping app, you can override the repository class and only return the shipping you want.
If you've defined your weight-based shipping through the dashboard, you can get it with the WeightBased model, and only return that:
forked_apps/shipping/repository.py:
from oscar.apps.shipping import repository
from oscar.core.loading import get_model
from . import methods
WeightBased = get_model('shipping', 'WeightBased')
class Repository(repository.Repository):
def get_available_shipping_methods(self, basket, user=None,
shipping_addr=None, request=None, **kwargs):
if shipping_addr:
weightbased_set = WeightBased.objects.all()
if weightbased_set:
return ( list(weightbased_set), )
# If no address was specified, or weight-based options are
# not available, return the "Reserve" shipping option
return ( methods.Reserve(), )
forked_apps/shipping/methods.py:
from oscar.apps.shipping import methods
class Reserve(methods.NoShippingRequired):
code = 'RESERVE'
name = 'Reserve'
description = 'Items will be reserved at the warehouse for 7 days'
Delaying payment would involve forking the payment app, and would be worth its own question.
The Oscar documentation also has some good information on further customization of shipping options in the "How to configure shipping" section.
Related
I am trying to scrape some information from flipkart.com for this purpose I am using Scrapy. The information I need is for every product on flipkart.
I have used the following code for my spider
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.selector import HtmlXPathSelector
from tutorial.items import TutorialItem
class WebCrawler(CrawlSpider):
name = "flipkart"
allowed_domains = ['flipkart.com']
start_urls = ['http://www.flipkart.com/store-directory']
rules = [
Rule(LinkExtractor(allow=['/(.*?)/p/(.*?)']), 'parse_flipkart', cb_kwargs=None, follow=True),
Rule(LinkExtractor(allow=['/(.*?)/pr?(.*?)']), follow=True)
]
#staticmethod
def parse_flipkart(response):
hxs = HtmlXPathSelector(response)
item = FlipkartItem()
item['featureKey'] = hxs.select('//td[#class="specsKey"]/text()').extract()
yield item
What my intent is to crawl through every product category page(specified by the second rule) and follow the product page(first rule) within the category page to scrape data from the products page.
One problem is that I cannot find a way to control the crawling and scrapping.
Second flipkart uses ajax on its category page and displays more products when a user scrolls to the bottom.
I have read other answers and assessed that selenium might help solve the issue. But I cannot find a proper way to implement it into this structure.
Suggestions are welcome..:)
ADDITIONAL DETAILS
I had earlier used a similar approach
the second rule I used was
Rule(LinkExtractor(allow=['/(.?)/pr?(.?)']),'parse_category', follow=True)
#staticmethod
def parse_category(response):
hxs = HtmlXPathSelector(response)
count = hxs.select('//td[#class="no_of_items"]/text()').extract()
for page num in range(1,count,15):
ajax_url = response.url+"&start="+num+"&ajax=true"
return Request(ajax_url,callback="parse_category")
Now i was confused on what to use for callback "parse_category" or "parse_flipkart"
Thank you for your patience
Not sure what you mean when you say that you can't find a way to control the crawling and scraping. Creating a spider for this purpose is already taking it under control, isn't it? If you create proper rules and parse the responses properly, that is all you need. In case you are referring to the actual order in which the pages are scraped, you most likely don't need to do this. You can just parse all the items in whichever order, but gather their location in the category hierarchy by parsing the breadcrumb information above the item title. You can use something like this to get the breadcrumb in a list:
response.css(".clp-breadcrumb").xpath('./ul/li//text()').extract()
You don't actually need Selenium, and I believe it would be an overkill for this simple issue. Using your browser (I'm using Chrome currently), press F12 to open the developer tools. Go to one of the category pages, and open the Network tab in the developer window. If there is anything here, click the Clear button to clear things up a bit. Now scroll down until you see that additional items are being loaded, and you will see additional requests listed in the Network panel. Filter them by Documents (1) and click on the request in the left pane (2). You can see the URL for the request (3) and the query parameters that you need to send (4). Note the start parameter which will be the most important since you will have to call this request multiple times while increasing this value to get new items. You can check the response in the Preview pane (5), and you will see that the request from the server is exactly what you need, more items. The rule you use for the items should pick up those links too.
For a more detail overview of scraping with Firebug, you can check out the official documentation.
Since there is no need to use Selenium for your purpose, I shall not cover this point more than adding a few links that show how to use Selenium with Scrapy, if the need ever occurs:
https://gist.github.com/cheekybastard/4944914
https://gist.github.com/irfani/1045108
http://snipplr.com/view/66998/
How can i use django templates to remove the contents of a page depending on the time of day?
I'm creating an online food delivery site. The delivery orders are sectioned off into times.
For example for a 7pm delivery drop, i would want the page to show normally until 6pm that day. Then at 6:01pm i would want the page to say something like "This delivery time is not available"
To be honest you shouldn't rely on template logic if you want to prevent unwanted behaviour.
You can create two variables, e.g.
from django.conf import settings
TIME_OPEN = getattr(settings, 'TIME_OPEN', datetime.now().replace(hour=10, minute=30, second=0, microsecond=0).time())
TIME_CLOSED = getattr(settings, 'TIME_CLOSED', datetime.now().replace(hour=21, minute=30, second=0, microsecond=0).time())
In your url patterns you could add something like:
if TIME_OPEN < datetime.now().time() < TIME_CLOSED:
urlpatterns += patterns('shop.customers.views',
(r'^checkout/$', 'checkout'),
)
Based on your new variables you could add a context_processor that supplies a context variable to each template for UI logic, e.g. {'shop_open': True}.
Mind you these examples rely on server time, so you would have to check, because it can differ from your local machine. Another approach could be to create a decorator which can be wrapped around views that require certain times.
So, just to make sure; Don't rely on template logic, protect your views
Is it possible to filter an inline autocomplete field by a dynamic value entered by a user?
For example, I have a an admin form where staff enters games information including home and visiting team, game date and time, score, etc. They also enter individual player names and stats. I would like to add a filter to show only the players on either the home or visiting team.
I am using the InlineAutocompleteAdmin module, which provides autocomplete hints for input fields.
Here is the current inline autocomplete code:
class IndividualFootballGameInline(InlineAutocompleteAdmin):
model = IndividualFootballGame
extra = 1
related_search_fields = {
'player': ('player__first_name', 'player__last_name', '#team__sport__sport=Football', '#team__season__season_start_date__year=' + str(get_current_season_start_year('football'))),
}
If this can be accomplished, can you explain how?
InlineAutocompleteAdmin provides a template that I modified to provide this functionality. The file templates/admin/autocomplete/inline_searchinput.html defines the jQuery lookup() function. I added additional code to check for values in the visiting and home teams field, and to append them to search_fields as needed.
Admin actions can act on the selected objects in the list page.
Is it possible to act on all the filtered objects?
For example if the admin search for Product names that start with "T-shirt" which results with 400 products and want to increase the price of all of them by 10%.
If the admin can only modify a single page of result at a time it will take a lot of effort.
Thanks
The custom actions are supposed to be used on a group of selected objects, so I don't think there is a standard way of doing what you want.
But I think I have a hack that might work for you... (meaning: use at your own risk and it is untested)
In your action function the request.GET will contain the q parameter used in the admin search. So if you type "T-Shirt" in the search, you should see request.GET look something like:
<QueryDict: {u'q': [u'T-Shirt']}>
You could completely disregard the querystring parameter that your custom action function receives and build your own queryset based on that request.GET's q parameter. Something like:
def increase_price_10_percent(modeladmin, request, queryset):
if request.GET['q'] is None:
# Add some error handling
queryset=Product.objects.filter(name__contains=request.GET['q'])
# Your code to increase price in 10%
increase_price_10_percent.short_description = "Increases price 10% for all products in the search result"
I would make sure to forbid any requests where q is empty. And where you read name__contains you should be mimicking whatever filter you created for the admin of your product object (so, if the search is only looking at the name field, name__contains might suffice; if it looks at the name and description, you would have a more complex filter here in the action function too).
I would also, maybe, add an intermediate page stating what models will be affected and have the user click on "I really know what I'm doing" confirmation button. Look at the code for django.contrib.admin.actions for an example of how to list what objects are being deleted. It should point you in the right direction.
NOTE: the users would still have to select something in the admin page, otherwise the action function would never get called.
This is a more generic solution, is not fully tested(and its pretty naive), so it might break with strange filters. For me works with date filters, foreign key filters, boolean filters.
def publish(modeladmin,request,queryset):
kwargs = {}
for filter,arg in request.GET.items():
kwargs.update({filter:arg})
queryset = queryset.filter(**kwargs)
queryset.update(published=True)
I have a fairly complex relationship that I am trying to make work with the Django admin site. I have spent quite some time trying to get this right and it just seems like I am not getting the philosophy behind the Django models.
There is a list of Groups. Each Group has multiple departments. There are also Employees. Each Employee belongs to a single group, but some employees also belong to a single Department within a Group. (Some employees might belong to only a Group but no Department, but no Employee will belong only to a Department).
Here is a simplified version of what I currently have:
class Group:
name = models.CharField(max_length=128)
class Department
group = models.ForeignKey(Group)
class Employee
department = models.ForeignKey(Department)
group = models.ForeignKey(Group)
The problem with this is that the Department select box on the Employees page must display all Departments, because a group has not yet been set. I tried to rectify this by making an EmployeeInline for the GroupAdmin page, but it is not good to have 500+ employees on a non-paginated inline. I must be able to use the models.ModelAdmin page for Employees (unless there is a way to search, sort, collapse and perform actions on inlines).
If I make EmployeeInline an inline of DepartmentAdmin (instead of having a DepartmentInline in GroupAdmin), then things are even worse, because it is not possible to have an Employee that does not belong to a Group.
Given my description of the relationships, am I missing out on some part of the Django ORM that will allow me to structure this relationship the way it 'should be' instead of hacking around and trying to make things come together?
Thanks a lot.
It sounds like what you want is for the Department options to only be those that are ForeignKey'ed to Group? The standard answer is that the admin site is only for simple CRUD operations.
But doing what you're supposed to do is boring.
You could probably overcome this limitation with some ninja javascript and JSON.
So first of all, we need an API that can let us know which departments are available for each group.
def api_departments_from_group(request, group_id):
departments = Department.objects.filter(group__id=group_id)
return json(departments) # Note: serialize, however
Once the API is in place we can add some javascript to change the <option>'s on the department select...
$(function() {
// On page load...
if ($('#id_group')) {
// Trap when the group box is changed
$('#id_group').bind('blur', function() {
$.getJSON('/api/get-departments/' + $('#id_group').val() + '/', function(data) {
// Clear existing options
$('#id_department').children().remove();
// Parse JSON and turn into <option> tags
$.each(data, function(i, item) {
$('#id_department').append('<option>' + item.name + '</option>');
});
});
});
}
});
Save that to admin-ninja.js. Then you can include it on the admin model itself...
class EmployeeAdmin(models.ModelAdmin):
# ...
class Media:
js = ('/media/admin-ninja.js',)
Yeah, so I didn't test a drop of this, but you can get some ideas hopefully. Also, I didn't get fancy with anything, for example the javascript doesn't account for an option already already being selected (and then re-select it).