@@ -1302,7 +1302,96 @@ def view(request):
13021302
13031303###** Dependency Inversion Principle (DIP)**
13041304
1305- * Coming soon*
1305+ > “Depend upon abstractions, not concrete details”,
1306+ > Uncle Bob.
1307+
1308+ Imagine we wanted to write a web view that returns an HTTP response that
1309+ streams rows of a CSV file we create on the fly. We want to use the CSV writer
1310+ that is provided by the standard library.
1311+
1312+ ** Bad**
1313+ ``` python
1314+ import csv
1315+ from ioimport StringIO
1316+
1317+
1318+ class StreamingHttpResponse :
1319+ """ A streaming HTTP response"""
1320+ ... # implementation code goes here
1321+
1322+
1323+ def some_view (request ):
1324+ rows= (
1325+ [' First row' ,' Foo' ,' Bar' ,' Baz' ],
1326+ [' Second row' ,' A' ,' B' ,' C' ,' "Testing"' ," Here's a quote" ]
1327+ )
1328+ # Define a generator to stream data directly to the client
1329+ def stream ():
1330+ buffer_= StringIO()
1331+ writer= csv.writer(buffer_,delimiter = ' ;' ,quotechar = ' "' )
1332+ for rowin rows:
1333+ writer.writerow(row)
1334+ buffer_.seek(0 )
1335+ data= buffer_.read()
1336+ buffer_.seek(0 )
1337+ buffer_.truncate()
1338+ yield data
1339+
1340+ # Create the streaming response object with the appropriate CSV header.
1341+ response= StreamingHttpResponse(stream(),content_type = ' text/csv' )
1342+ response[' Content-Disposition' ]= ' attachment; filename="somefilename.csv"'
1343+
1344+ return response
1345+
1346+ ```
1347+
1348+ Our first implementation works around the CSV's writer interface by manipulating
1349+ a` StringIO ` object (which is file-like) and performing several low level
1350+ operations in order to farm out the rows from the writer. It's a lot of work
1351+ and not very elegant.
1352+
1353+ A better way is to leverage the fact that the writer just needs an object with
1354+ a` .write() ` method to do our bidding. Why not pass it a dummy object that
1355+ immediately returns the newly assembled row, so that the` StreamingHttpResponse `
1356+ class can immediate stream it back to the client?
1357+
1358+ ** Good**
1359+ ``` python
1360+ import csv
1361+
1362+
1363+ class Echo :
1364+ """ An object that implements just the write method of the file-like
1365+ interface.
1366+ """
1367+ def write (self ,value ):
1368+ """ Write the value by returning it, instead of storing in a buffer."""
1369+ return value
1370+
1371+ def some_streaming_csv_view (request ):
1372+ """ A view that streams a large CSV file."""
1373+ rows= (
1374+ [' First row' ,' Foo' ,' Bar' ,' Baz' ],
1375+ [' Second row' ,' A' ,' B' ,' C' ,' "Testing"' ," Here's a quote" ]
1376+ )
1377+ writer= csv.writer(Echo(),delimiter = ' ;' ,quotechar = ' "' )
1378+ return StreamingHttpResponse(
1379+ (writer.writerow(row)for rowin rows),
1380+ content_type = " text/csv" ,
1381+ headers = {' Content-Disposition' :' attachment; filename="somefilename.csv"' },
1382+ )
1383+
1384+ ```
1385+
1386+ Much better, and it works like a charm! The reason it's superior to the previous
1387+ implementation should be obvious: less code (and more performant) to achieve
1388+ the same result. We decided to leverage the fact that the writer class depends
1389+ on the` .write() ` abstraction of the object it receives, without caring about
1390+ the low level, concrete details of what the method actually does.
1391+
1392+ This example was taken from
1393+ [ a submission made to the Django documentation] ( https://code.djangoproject.com/ticket/21179 )
1394+ by this author.
13061395
13071396** [ ⬆ back to top] ( #table-of-contents ) **
13081397