Document not found (404)
+This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..f173110 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ +This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/404.html b/404.html new file mode 100644 index 0000000..8851eea --- /dev/null +++ b/404.html @@ -0,0 +1,190 @@ + + +
+ + +This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +Please submit bug reports, +feature requests, +or general feedback to our +Bug Tracker on GitHub.
+The easiest way to contribute to this project +is by clicking on the button in the top right corner of any page. +This will open the corresponding page on GitHub, +where you can add or modify content +and then commit the change.
+For more elaborated changes +please:
+Special thanks to Kevin Amado1 for writing these docs.
+VP Development 2022. Feel free to connect with me on LinkedIn or GitHub, or read my personal website.
+This project is released under either
+the Creative Commons CC0 1.0 Universal
license
+and/or under the The Unlicense
license,
+at your discretion,
+whose verbatim copies can be found below.
The Unlicense
+-------------
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org>
+
+Creative Commons Legal Code
+---------------------------
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
+
+
+ Tech Start's Django Guide |
Django is a free and open source python framework that lets you build awesome backends for websites, apps, and more. You can use it to host databases and build secure APIs for them without writing a line of SQL. You can also use it to create multi-page applications with dynamically served content. It makes it easy to get started building complex functionalities by automating a lot of the boilerplate code that you'd normally have to write.
We recommend that you have a basic knowledge of python before using django! This will help you debug any errors that you get.
(Img src: https://medium.com/crowdbotics/when-to-use-django-and-when-not-to-9f62f55f693b)
Before you get started with Django, you should set up your environment. You should have a recent version of Python installed. You can follow the directions here:
https://docs.djangoproject.com/en/3.1/howto/windows/
You should make a venv for your django project.
Video tutorial for venv windows: https://www.youtube.com/watch?v=APOPm01BVrk
Video tutorial for venv mac/linux: https://www.youtube.com/watch?v=Kg1Yvry_Ydk
Note: if you're using Git Bash on windows, use $ source venv/scripts/activate
When you're done using your virtual environment, just enter $ deactivate
https://docs.djangoproject.com/en/3.1/intro/tutorial01/
$ python -m pip install Django
Get Django installed once you have created your virtual environment.
$ python -m django --version
// Checks if you have django installed
Next, cd into the folder which you want to contain your project and run the following command:
$ django-admin startproject pNameHere
// starts project. cd into pNameHere folder.
Good to know: Projects vs. apps What’s the difference between a django project and a django app? An app is a Web application that does something – e.g., a Weblog system, a database of public records or a small poll app. A project is a collection of configuration and apps for a particular website. A project can contain multiple apps. An app can be in multiple projects. |
$ python manage.py startapp yourAppName
This creates an app within your project. You'll need to be in the same folder that holds your project's manage.py
Next step: include your app in the INSTALLED_APPS fields in settings.py (just the name)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
#Add your app name like this!
'myAppName',
]
Models allow you to define the content of your database. If you don't need content in your database, you won't need models.
You can follow along with this section here:
https://docs.djangoproject.com/en/3.1/intro/tutorial02/
More about models: https://docs.djangoproject.com/en/3.1/topics/db/models/
You will define all your models in models.py, located within the folder for your app.
from django.db import models
# Create your models here.
class Album(models.Model):
name = models.CharField(max_length=200)
artist = models.CharField(max_length=100)
year_released = models.DateField()
def __str__(self):
return str(self.name)
class Song(models.Model):
song_name = models.CharField(max_length=100)
album = models.ForeignKey(Album, on_delete=models.CASCADE)
num_streams = models.IntegerField()
def __str__(self):
return str(self.song_name)
Each model should correspond to the structure of a table of a relational model of a database. If you don't know what this means, ask someone who has taken CPSC 471 (or an equivalent databases course)
Django can convert these into real SQL tables!
There are more options that can be explored about how you can define your models, but this should be a good base for you to do your own research :)
Now we're ready to convert these into a real database! By default, Django will make a sqlite file that has your database.
Converting models into your database https://docs.djangoproject.com/en/3.1/intro/tutorial02/ >> python manage.py makemigrations appName Creates migrations for the changes you made in appName >> python manage.py migrate Migrates the changes you made into your database |
Whenever you are ready to run your server, just call this command!
>> python manage.py runserver
By default, this will run the Django server on localhost:8000. View the django documentation to see how you can run it on a different port. You can now access it from your web browser by visiting http://localhost:8000 !
You can also create a superuser (admin) to view the inner contents of your database. To do this, you first need to create them from the command line using the following command:
>> python manage.py createsuperuser --username yourNameHere --email yours@email.ca
This will create a super user with your provided info (it will prompt you to enter a password as well).
The following command creates a token for the superuser that you can use for authentication in requests. If you are not using Django Rest Framework, this is not applicable to you.
>> python manage.py drf_create_token yourSuperUserName
Note: if you're trying to run these for your deployed app on heroku, you need to prepend heroku run before those commands! See the Heroku section for a description on how you can deploy it.
You can see the admin page of your website to view the inner content of your database. This is automatically created by Django. Visit http://localhost:8000/admin and enter your passcode.
If you want your models to show up in the admin page, you will need to specify them in admin.py like this:
from django.contrib import admin
from .models import Album, Song
# Register your models here.
admin.site.register(Album)
admin.site.register(Song)
Once you log in to the admin site, you should see something like this. From here, you can add & remove database entries.
URLs allow you to define the paths that exist in your system, and what happens when you call them.
URLs documentation: https://docs.djangoproject.com/en/3.1/ref/urls/
How URLs are processed in Django: https://docs.djangoproject.com/en/3.1/topics/http/urls/#how-django-processes-a-request
Read more: https://docs.djangoproject.com/en/3.1/intro/tutorial03/
If you're constructing a big application, it's standard practice in django to include different apps for each part of your system, and link them to the main project.
However, since we're only making small-scale side-projects, it's fine to ignore this best-practice and include everything in a single app. Just understand that in a large industrial scale project you wouldn't necessarily want to do this.
// urls.py in a project:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('myApp/', include('myApp.urls'))
]
// example urls.py in myApp folder:
from django.urls import path
from . import views
urlpatterns = [
path('hello_world', views.ping, name='Hello World!'),
path('hello-x/<str:hello_to>', views.hellox, name='Hello to x'),
path('hello-x/<print_me>/print', views.printx, name='Print this!'),
path('goodbye', views.goodbye, name='goodbye'),
]
Now you can visit a path using http://localhost:8000/myApp/hello-world, for example.
Views allow you to define what happens when you access a certain url in your system (using your browser, an API tool like Postman, or something else altogether). In your views, you could define interactions with the model (your database) or entirely different interactions altogether. You can use the definition of the view to call external processes.
If you want to make more complicated views and understand the Request and Response items, read this:
https://docs.djangoproject.com/en/3.1/ref/request-response/
To understand views more in-depth, read the documentation: https://docs.djangoproject.com/en/3.1/topics/http/views/
Here are some simple examples of what you can do with a view. Note that these are just examples and don't represent best practice at all.
from django.http import HttpResponse, response
# views.py
def ping(request):
myRes = "Hello World!"
return HttpResponse(myRes)
def hellox(request, hello_to):
myRes = {"My Reply": "Hello " + hello_to}
return response.JsonResponse(myRes)
def printx(request, print_me):
print("Hello to " + print_me)
return response.HttpResponseNotFound("I printed it!")
def goodbye(request):
if not (request.method == 'GET'):
return response.HttpResponseBadRequest()
queryParams = request.GET
msg = queryParams.get('msg', "Gamers")
return response.JsonResponse({"Reply": "Goodbye " + msg})
Now, we want to adhere to DRY (Don't repeat yourself) when creating views. Therefore, it is almost always best to define your views as Class-Based views (CBVs) which handle more of the boiler plate code for you and help ensure your views follow standards.
Please read more about class-based views here: https://docs.djangoproject.com/en/3.1/topics/class-based-views/
Both the above docs and the docs for views also show how you can interact with your database items through a view. But, if you're building an API, I highly recommend using the tools in the following section: Django REST Framework.
Once you have defined your views and given them a corresponding url, you can test them out.
>> python manage.py runserver
Run your server, and using either a web browser, or preferably an API testing tool like Postman (https://www.postman.com/) access the proper urls (ex. http://localhost:8000/myApp/hello_world) to see if they have the expected behavior.
Django REST Framework is an add-on to Django that makes it simple to develop REST-compliant APIs. There is great documentation here: https://www.django-rest-framework.org/ <--- FOLLOW INSTALL INSTRUCTIONS
What is a RESTful framework? Learn more here: https://restfulapi.net/
Django REST Framework provides you with tools to make class-based views to easily implement database CRUD (Create Read Update Destroy) operations, as well as define more complex operations.
Before we define any endpoints with Django REST Framework, let's make some serializers.
Django REST Framework uses serializers as a way to perform translation of your models from your python code and your database into data formats like JSON and XML that an API might use. Read more about them here:
https://www.django-rest-framework.org/api-guide/serializers/
We should define some basic serializers so that we can make API endpoints that interact with the content of our database models.
Here's an example, using the Song and Album models we defined earlier. Here's what's at the top of serializers.py:
from rest_framework import serializers
from .models import *
class SongSerializer(serializers.ModelSerializer):
class Meta:
model = Song
fields = ("id", "song_name", "num_streams")
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = ("name", "year_released", "artist", "id")
Make sure your fields match exactly the names that you used in your models.
You may be curious why I also included an id, when we didn't define one in our models- this is because Django auto generated an id for us in this models because we didn't specify a primary key. This id field always has the name id. It is often useful for our API, so we'll include it.
We can also create multiple serializers for the same models, if we wanted different behavior. For example, what if we wanted to include the album id of the song?
class SongSerializerWithAlbumId(serializers.ModelSerializer):
class Meta:
model = Song
fields = ("id", "song_name", "num_streams", "album")
This would include the album's PK (in this case, it's id, but if the PK was different, it'd be something else).
What if we wanted to include the full album info when an api request was made to see the song? Here's another example serializer that we could make:
class SongSerializerFullAlbum(serializers.ModelSerializer):
myFullAlbumDesc = AlbumSerializer("album", read_only=True)
class Meta:
model = Song
fields = ("id", "song_name", "num_streams", "myFullAlbumDesc")
It's using our album serializer from earlier to serialize a field, which must (read only is an optional parameter that makes it so that it's only included in reading requests, not create/update/destroy.)
This was just an introduction to serializers. If you want to use more complex behaviors, you'll have to do the research on your own.
Pre-requisite to this section: understand URLS and views in vanilla Django, and read the serializers section |
More reading: https://www.django-rest-framework.org/tutorial/3-class-based-views/
Video overview of similar topic: https://www.youtube.com/watch?v=akvFA5VMXJU
You can use Django's Class Based Views to quickly create views that can do CRUD (Create, Read, Update, Destroy) operations on your database.
In views.py:
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import status
from .models import *
from .serializers import *
Some class based views that we'll define. Right now these are just the generic create, read, update, destroy views. By defining these views with the classes, Django REST Framework takes care of the default behavior for us. It's that easy!
class SaveSong(generics.CreateAPIView):
queryset = Song.objects.all()
serializer_class = SongSerializerWithAlbumId
class GetSongs(generics.ListAPIView):
queryset = Song.objects.all()
serializer_class = SongSerializer
class DeleteSong(generics.DestroyAPIView):
queryset = Song.objects.all()
serializer_class = SongSerializer
class UpdateSong(generics.RetrieveUpdateAPIView):
queryset = Song.objects.all()
serializer_class = SongSerializerWithAlbumId
Notice that we need to make the create and update serializers include the album ID- if we didn't then you couldn't create song objects since their album id must be not null.This same principal applies to any model that has a foreign key which isn't allowed to be null.
Before we can use the views we created, we need to hook them up to a URL, just like you would for any other view. Do keep in mind that we need to call the as-view function on them, though. Here is an example of the URLs for the previous views. This pattern is how we normally define CRUD endpoint urls for any entity in a database
path('song', views.GetSongs.as_view(), name='songs'),
# Create a song
path('song/create', views.SaveSong.as_view(), name='Save Song'),
#Updates a specified license with information
path('song/<int:pk>', views.UpdateSong.as_view(), name='Update Song'),
# Deletes a song specified by pk
path('song/<int:pk>/delete', views.DeleteSong.as_view(), name='Delete Song'),
If you are using a pk that is not an int (you manually defined a pk instead of using the default id generated), you'll have to specify that accordingly.
What if we want more complex behavior beyond the default predefined classes? We can modify them to add more conditions to what is returned.
In this example, we added an optional way to filter songs by album, using a query_param called album. You'll need to read documentation and tutorials if you want to know more about the custom behavior you can define within your Django REST Framework views.
class GetSongInAlbum(generics.ListAPIView):
serializer_class = SongSerializer
def get_queryset(self):
queryset = Song.objects.all()
alb = self.request.query_params.get('album', None)
queryset = queryset.filter(album=alb)
return queryset
If you have a view that isn't necessarily linked to CRUD actions, or has more complex usage and needs more custom defined behavior, you can use APIView.
Compile and run your app with
$ python manage.py runserver
Use your bugfixing wizardry to fix any errors that might show up. Now you should be ready to give those predefined endpoints you made for a spin!
Here's some examples that I did using Postman for API testing. If you used Django REST Framework, it should also come with a built-in API testing tool that you can use in your browser.
Here's a simple GET request. This is a database read operation, and it's pretty simple. Your browser is making GET requests to every URL you visit while you surf the web.
Request | |
Response |
Here's a POST request (it's post because we're creating or Posting new data) to our create route. We should include the key-value pairs for the song we want to create in the Body of our request.
Request | |
Response |
To update, let's follow the URL pattern we defined with the pk of the song we want to update. We can use PUT or PATCH. The info you're sending should be in the Body of the request, just like it was for our POST request.
Request | |
Response |
Let's do the same thing for our deleteSong view, but let's delete Taylor's song this time (pk: 2). I'm sure it was no good anyways.
Request | |
Response |
Let's use our GET view to see what's inside the DB now:
**unimportant note: in my zeal to delete taylor's song I had a mishap and accidentally deleted song 3, which I have readded here using a post request. but it's id is now 5 :[
Finally, let's try out that "song with album" route. We'll add it to our urls.py:
# Probably not the best naming convention
path('songInAlbum', views.GetSongInAlbum.as_view(), name='Get song in album'),
Here's what our request will look like. ^^^^^^^^
Here's the response:
Good to know: Query Parameters Notice how our query params don't have to be specified in urls.py - they are dynamically generated from the URL that we try access (everything that comes after a ? in a url is a query parameter, with keys and values separated by '='. If you had multiple query parameters they would be separated by '&'. Next time you're browsing the web, notice how query parameters are used across the different websites you visit! It's easy to access query params within Django - see the getSongInAlbum view definition for an example. |
Up to now, we've covered the fundamentals of how to create a database, populate it, and create simple endpoints for creating, updating, and destroying. But what happens when we want our system to be used by real users? How do we store their information and their interactions with our system? There are a few important issues that we'll need to address:
The answer to these questions can be complicated. In order to save your time and energy, we're going to utilize the resources that Django and Django REST Framework provide for us as much as possible instead of developing our own solutions. Not only is this easier, but it's also much more secure- would you trust a system written from scratch by a novice undergrad student with your password and financial information?
How do we store user's personal info?
The answer to this question is usually to use Django's built-in User model. You can read the docs on User models here:
https://docs.djangoproject.com/en/3.1/ref/contrib/auth/
The User model contains common fields that will be used by users, and in your serializers you can define which fields are relevant to your use case.
By default, Django builds the User models for you. You can see them after you runserver and check inside the /admin route.
We can also utilize the User model to build new endpoints in our API, just like we could with any other model. Here's an example:
## Models.py
from django.contrib.auth.models import User
…
class UserLikesSong(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
song = models.ForeignKey(Song, on_delete=models.CASCADE)
## Serializers.py
class UserLikesSongSerializer(serializers.ModelSerializer):
class Meta:
model = UserLikesSong
fields = ("id", "user", "song")
#Id of the rel, Id of the user, ID of the song
You can now make endpoints with this just like you would with any other model/serializer. This specific example could be used to track what songs the User likes, like in Spotify.
If you wanted to make a custom User model, you could read more about it here https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html and do more research, as there are many methods you could use. For basic university usage though, it's 99% of the time going to be faster and easier to roll with the User model they give you out of the box.
If you want to give different categories of users different permissions, see the permissions section (TODO: this won't be done for a while. In the meantime, these links may help: https://www.django-rest-framework.org/api-guide/permissions/ ← Technical overview
https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/ ← Basic usage example)
Signup, Login, Sessions: How do we do them?
I highly recommend using Django REST Framework's Authtokens to handle information about user sessions. You can read about authtokens, as well as the other options available, here: https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
To add Authtoken's, make sure the following items appear in settings.py:
###### You will need to add the REST Framework part.
###### INSTALLED_APPS should already exist.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
INSTALLED_APPS = [ # There will be more here
'rest_framework',
'rest_framework.authtoken',
]
Note: I couldn't get these to work, at least not with authtoken. Leaving them here in case some enterprising individual finds them useful, or message us on Discord if you figure this out :) To get REST Framework's default login and logout views (prebuilt), type this in your project's root urls.py file: urlpatterns = [ ... path('api-auth/', include('rest_framework.urls')) ] Your path doesn't have to be api-auth, it can be whatever you want. |
To use REST Framework's login view, include this in urls.py:
path('whateverPathYouWantToLogin', obtain_auth_token, name='API Token Login'),
Include this at top of your urls.py:
from rest_framework.authtoken.views import obtain_auth_token
When you access this path and provide a real username and password in the request body, then you should receive an authtoken. This authtoken is associated with your account. Store this authtoken in a safe place. Now, you can use it in the "authorization" section of your next HTTP requests, and all requests you make from the client will be associated with the account you just logged in from.
Creating views for signing up is more difficult.
In serializers.py: from django.contrib.auth.models import User class RegisterSerializer(serializers.ModelSerializer): class Meta: model = User fields = ('id','username','password')#Change fields if u want extra_kwargs = { 'password':{'write_only': True}, } def create(self, validated_data): user = User.objects.create_user(validated_data['username'], password = validated_data['password']) return user This serializer will make sure that the password that the user makes is valid, and that it's write-only for security purposes. Choose which fields us |
In views.py: from django.contrib.auth import get_user_model # If used custom user model from rest_framework import permissions #We'll discuss more about perms later … class RegisterUserView(generics.CreateAPIView): model = get_user_model() #Will get the right one if you use custom permission_classes = [ permissions.AllowAny # Or anon users can't register ] serializer_class = RegisterSerializer #What we defined above |
In urls.py: path('register', views.CreateUserView.as_view(), name='Register user'), (you may want to put your register / login views together in a different Django App (so they are in a distinct section of your API) |
Test Request in Postman: Response from request: |
Now let's quickly do a login from this user we just created!
I did a login request to the login view I made earlier, but here's what I got:
Whenever you see an error like "no such table", that should be a clue that you need to rerun migrations. The app expected there to be a SQL table, but there was none made yet! Running migrations will ensure there is. Recall the commands for migrations are:
>> python manage.py makemigrations yourAppName
>> python manage.py migrate
In this case, just the second command will be sufficient
Request:
Response:
Yay! It worked! Now we can include this token in our request headers to associate all future requests made with the user we logged in.
In future requests, you should put the token as a value in your request headers, using the key: token.
Depending on the front-end you build, you should use a different way to store the authtoken that you get from logging in. Usually storing in local memory is okay. Do your own research for how to store authtokens with whatever system you are using.
If you want to improve security further, you can use JWT (JSON webtoken) instead, following the instructions here: https://simpleisbetterthancomplex.com/tutorial/2018/12/19/how-to-use-jwt-authentication-with-django-rest-framework.html
How do we make endpoints behave differently depending on which user is accessing them?
If a user makes a request while they are authenticated (using authtoken, or some other alternative method), then the system will automatically know what user is associated with the user who made the request.
You can access the user within a class-based view through
self.request.user
You can use this within your views in a variety of ways: to filter, to make more complex queries, and to check if the user should have access.
For example, let's make a UserLikesSong endpoint that is limited to the songs that the currently logged in user has liked.
class GetUserLikesSongs(generics.ListAPIView):
def get_queryset(self):
queryset = UserLikesSong.objects.all()
queryset = queryset.filter(user=self.request.user)
# Leftside of filter: from queryset. Rightside: how we're filtering
return queryset
serializer_class = UserLikesSongSerializer
We'll cover this in much more detail in the Permissions section.
NOTE: Everything past here is incomplete - you will need to supplement it with your own research, like I did to make this guide! |
To use generic permissions with Django, all you need to do is:
from rest_framework.permissions import IsAdminUser, IsAuthenticated, IsAuthenticatedOrReadOnly
Now, on any class-based view you want to guard with permissions, you can add the following line:
class deleteLicenseType(generics.DestroyAPIView):
permission_classes = [IsAdminUser]
queryset = License_Type.objects.all()
serializer_class = License_TypeSerializer
(this is from a different project)
You can apply multiple permissions to the same view like this:
permission_classes = [IsAdminUser|IsAuthenticatedOrReadOnly]
Sessions/cookies are very easy to make use of with Django. You can use cookies to store information in a user's browser that you'll be able to access in all subsequent requests that a user makes. One example of a good use of sessions/cookies is to store a user's shopping cart content.
Some great videos for learning about sessions & cookies:
https://www.youtube.com/watch?v=C75IW38hKI8
https://www.youtube.com/watch?v=RjykNmVdcgI
To get your projects online, you can deploy them to Heroku. Heroku is just one of several possible hosting services for Django- but it's base tier is free, easy to use, and simple to deploy to, so that's what I recommend you use. The biggest downside of using Heroku is that its free tier will automatically shut down your app after a period of downtime, meaning it'll take a long time to respond the next time you try to access it.
A guide on getting started:
https://devcenter.heroku.com/articles/django-app-configuration
Some useful commands:
>> pip install gunicorn
To deploy to Heroku, you will need to make a file called Procfile (no file ending), and add the following gunicorn text to it:
web: gunicorn yourAppName.wsgi
This gunicorn file should be at the same level as your manage.py file. When you deploy to Heroku, you should be deploying from this level of the project hierarchy to avoid issues.
Your remote heroku environment needs to understand what requirements it will need to have to start up. You can do this by providing it with a requirements.txt file which will also be at the same level as your manage.py file.
To get the right requirements in a .txt file, type
>> pip freeze > requirements.txt
These commands will help initialize your heroku repository:
>> heroku create
>> heroku run python manage.py migrate
>> heroku run python manage.py createsuperuser
Important: Your database itself will not transfer to Heroku. You will need to recreate all entities, config, and users.
\ No newline at end of file diff --git a/guides/Django_Guide/images/image1.png b/guides/Django_Guide/images/image1.png new file mode 100644 index 0000000..c2dc3dd Binary files /dev/null and b/guides/Django_Guide/images/image1.png differ diff --git a/guides/Django_Guide/images/image10.png b/guides/Django_Guide/images/image10.png new file mode 100644 index 0000000..7e1259b Binary files /dev/null and b/guides/Django_Guide/images/image10.png differ diff --git a/guides/Django_Guide/images/image11.png b/guides/Django_Guide/images/image11.png new file mode 100644 index 0000000..06dbb24 Binary files /dev/null and b/guides/Django_Guide/images/image11.png differ diff --git a/guides/Django_Guide/images/image12.png b/guides/Django_Guide/images/image12.png new file mode 100644 index 0000000..49db837 Binary files /dev/null and b/guides/Django_Guide/images/image12.png differ diff --git a/guides/Django_Guide/images/image13.png b/guides/Django_Guide/images/image13.png new file mode 100644 index 0000000..9161cbb Binary files /dev/null and b/guides/Django_Guide/images/image13.png differ diff --git a/guides/Django_Guide/images/image14.png b/guides/Django_Guide/images/image14.png new file mode 100644 index 0000000..2c27cf4 Binary files /dev/null and b/guides/Django_Guide/images/image14.png differ diff --git a/guides/Django_Guide/images/image15.png b/guides/Django_Guide/images/image15.png new file mode 100644 index 0000000..12e2a36 Binary files /dev/null and b/guides/Django_Guide/images/image15.png differ diff --git a/guides/Django_Guide/images/image16.png b/guides/Django_Guide/images/image16.png new file mode 100644 index 0000000..160f9db Binary files /dev/null and b/guides/Django_Guide/images/image16.png differ diff --git a/guides/Django_Guide/images/image17.png b/guides/Django_Guide/images/image17.png new file mode 100644 index 0000000..568218d Binary files /dev/null and b/guides/Django_Guide/images/image17.png differ diff --git a/guides/Django_Guide/images/image18.png b/guides/Django_Guide/images/image18.png new file mode 100644 index 0000000..ad52aa4 Binary files /dev/null and b/guides/Django_Guide/images/image18.png differ diff --git a/guides/Django_Guide/images/image19.png b/guides/Django_Guide/images/image19.png new file mode 100644 index 0000000..edca256 Binary files /dev/null and b/guides/Django_Guide/images/image19.png differ diff --git a/guides/Django_Guide/images/image2.png b/guides/Django_Guide/images/image2.png new file mode 100644 index 0000000..a8b2956 Binary files /dev/null and b/guides/Django_Guide/images/image2.png differ diff --git a/guides/Django_Guide/images/image20.png b/guides/Django_Guide/images/image20.png new file mode 100644 index 0000000..1944723 Binary files /dev/null and b/guides/Django_Guide/images/image20.png differ diff --git a/guides/Django_Guide/images/image21.png b/guides/Django_Guide/images/image21.png new file mode 100644 index 0000000..c98ca33 Binary files /dev/null and b/guides/Django_Guide/images/image21.png differ diff --git a/guides/Django_Guide/images/image22.png b/guides/Django_Guide/images/image22.png new file mode 100644 index 0000000..c3587b1 Binary files /dev/null and b/guides/Django_Guide/images/image22.png differ diff --git a/guides/Django_Guide/images/image23.png b/guides/Django_Guide/images/image23.png new file mode 100644 index 0000000..cd9a988 Binary files /dev/null and b/guides/Django_Guide/images/image23.png differ diff --git a/guides/Django_Guide/images/image24.png b/guides/Django_Guide/images/image24.png new file mode 100644 index 0000000..1eed90b Binary files /dev/null and b/guides/Django_Guide/images/image24.png differ diff --git a/guides/Django_Guide/images/image25.png b/guides/Django_Guide/images/image25.png new file mode 100644 index 0000000..38bb54c Binary files /dev/null and b/guides/Django_Guide/images/image25.png differ diff --git a/guides/Django_Guide/images/image26.png b/guides/Django_Guide/images/image26.png new file mode 100644 index 0000000..8fa6fbc Binary files /dev/null and b/guides/Django_Guide/images/image26.png differ diff --git a/guides/Django_Guide/images/image3.png b/guides/Django_Guide/images/image3.png new file mode 100644 index 0000000..00bbd2d Binary files /dev/null and b/guides/Django_Guide/images/image3.png differ diff --git a/guides/Django_Guide/images/image4.png b/guides/Django_Guide/images/image4.png new file mode 100644 index 0000000..f1f1d0e Binary files /dev/null and b/guides/Django_Guide/images/image4.png differ diff --git a/guides/Django_Guide/images/image5.png b/guides/Django_Guide/images/image5.png new file mode 100644 index 0000000..34d45f4 Binary files /dev/null and b/guides/Django_Guide/images/image5.png differ diff --git a/guides/Django_Guide/images/image6.png b/guides/Django_Guide/images/image6.png new file mode 100644 index 0000000..df82768 Binary files /dev/null and b/guides/Django_Guide/images/image6.png differ diff --git a/guides/Django_Guide/images/image7.png b/guides/Django_Guide/images/image7.png new file mode 100644 index 0000000..21c6f71 Binary files /dev/null and b/guides/Django_Guide/images/image7.png differ diff --git a/guides/Django_Guide/images/image8.png b/guides/Django_Guide/images/image8.png new file mode 100644 index 0000000..1068a4c Binary files /dev/null and b/guides/Django_Guide/images/image8.png differ diff --git a/guides/Django_Guide/images/image9.png b/guides/Django_Guide/images/image9.png new file mode 100644 index 0000000..6539748 Binary files /dev/null and b/guides/Django_Guide/images/image9.png differ diff --git a/guides/Git_Guide/GitGuide.html b/guides/Git_Guide/GitGuide.html new file mode 100644 index 0000000..f2ef5e6 --- /dev/null +++ b/guides/Git_Guide/GitGuide.html @@ -0,0 +1 @@ +
Tech Start's Git Guide |
Legend
=== Beginner Section ===
=== Advanced Section ===
Unless otherwise noted, all git commands are one line terminal commands
We are also assuming that you have set up a project at /path/to/project and had “cd’ed” to /path/to/project
https://docs.github.com/en/enterprise-server@2.22/get-started
A Git Repository is virtual storage of your code, allowing you to save versions of your code, as well as share and allow others to edit the code.
Initializing a new repository: git init
Cloning an existing repository: git clone [github git link]
Imagine that we have two people working on the same paper remotely, Person A and Person B. Now we notice that Person B is the laziest of the two, so Person A starts the paper.
Since they are unaware of Google Docs, Person A choses to create a word document on their local machine. This can be seen as Initializing a new repository.
After working on the paper for a bit, he realizes that Person B also needs to contribute, so they send the paper by email to Person B. This step is equivalent to forking a repository.
Person B decides that he would prefer to work on the paper by hand, and so he takes the email that Person A sent, and prints it to work on, cloning the repository
Git works using units of change called commits. These commits are essentially snapshots of your project. To share any changes, additions, and deletions that you make with other people and upload it to the internet, you will need to package them into a commit first.
The process of staging files to create a commit is like an assembly line at a factory. The final product of this process is a commit, and you can use commands like
You can think of the staging area of git (the green box that says "staged changes" in the below diagram) like a box. You can add and remove changes from the box on a per-file-basis.
Committing is like sealing that box and sticking a label on it. The contents of that box are your changes. But, if there are changes in the untracked or unstaged areas, they will not be included in the commit, because they are outside of the box.
The following diagram shows how the staging process works, and what commands you can use to move changes on a per-file-basis in between areas. Use the git status command to see a summary of where every change in your project is on this diagram! We recommend using git status frequently.
Shown are some common commands for using git to store and prepare the code to be pushed to the remote repository. They are shown in the general order that you want to use them in.
Below is the legend for some common, optional parameters which are shown
git status | Shows the status of all changes (changes that have been staged, NOT been staged, or NOT yet been committed). It shows where every change is on the diagram above, and even lists some helpful commands. It will also say what branch you are on, and if you are ahead or behind the remote version of your branch in terms of commits (more on this in later sections). Additionally, if you have a merge conflict, it will show which files caused it. |
git add [file path] | selects the specified files, moves it to the “staging area” and includes it in the next commit This command will also allow adding a deleted file to be staged, which after being committed and pushed will remove the file from the git branch **This command will ignore all files in the “.gitignore” file **
|
git commit -m “[commitMessage]” | Creates a new commit that includes all changes that you added to the staging area. You always need to include a name for your commit. It is helpful to be as descriptive as possible! If you don't use the -m flag and provide a commit message in quotations, git will make you write a commit message using a text editor. However, this can be very confusing for people especially since your default git text editor is often configured to be VIM (if it is, you can type :qa to exit). For this reason, we recommend always specifying the commit message using -m. After you commit, these changes are no longer in the staging area- they are in your commit!
|
git restore [file path] | Discards local changes in a file, thereby restoring its last committed state. You can use it to quickly get rid of accidental or unnecessary changes, restoring your files to how they used to be before you changed them. It won't work if your file is already staged - you'll have to unstage it with git restore --staged first. |
git restore --staged [file path] | Removes the file from the Staging Area, but preserve all modifications you made to it. You can use it if you accidentally added a file to the staging area whose changes shouldn't be included as part of the next commit you are planning to make. If the file was originally untracked, it becomes untracked again. If it was originally a file with unstaged changes, the changes become unstaged again. |
Naming commits
When you create a commit, you always want to include a descriptive name for the commit that describes exactly what it accomplishes. You wouldn’t label a moving box with kitchen items as simply “stuff.”
For a video tutorial on staging files, watch https://www.youtube.com/watch?v=KngvG8WzYLU&ab_channel=TheNetNinja
If you want to learn about additional flags and commands you can use in the process of staging files and adding commits, see the section Advanced Staging & Commits
Branches represent an independent version of the code. They allow new features to be worked on, while ensuring that a working version of the code is not tampered with. This allows large changes to be made to the code base, with little fear of breaking larger projects.
git branch | Lists all branches in the current repository |
git branch [branchName] | Creates a branch called branchName in the current repository. The created branch's commit history will match the branch you were on when you called git branch. We recommend naming branches according to what you want your branch to do. For example, if I was updating the font on a website, I might call my branch updateFont. You may also want to add a prefix to the branch name to indicate it is your branch (this is optional- example, I could call my branch joel/updateFont.) |
git branch -d [branchName] | Deletes the branch called branchName (You can use -D instead of -d to force delete the specified branch, even is it has unmerged changes. It's typically better to use -d, unless you are 100% sure you will never need the branch you are deleting again) |
git checkout [branchName] | Navigates your current directory to the specified branch, allows you to select which line of development you are working on. You can only switch branches if you have no unstaged/staged changes in your current branch. If you can't switch branches because of this, see What happens if you can't checkout? for more instructions. |
How should you use branches?
Whenever you are working on a new feature, or conducting a test/messing with code, you should create a new branch to use.
Before you make your branch, you should make sure you are creating your branch based on the most recent code. Do git checkout main (or master instead of main) to switch to the primary branch of your repository. You will also probably want to do git pull to make sure the primary branch is up to date with the version present on your remote repository (more on this in the next section).
Now that you are on the primary branch, use git branch [branchName] to create a new branch based on the current one. Make sure you name it according to what you aim to accomplish there (see the description of the command above).
Now that you have created your branch, you'll want to switch to it. Use git checkout [branchName] to switch to your branch. You can do work here and follow the instructions in the staging files and creating commits section to save your changes into commits.
Eventually, you'll be done using the branch (perhaps you will follow the instructions in the next few sections to push it to your remote repository and use it in a pull request. or perhaps you need to work somewhere else). Either way, you can switch to a different branch with git checkout [branchName].
If you have completed a pull request for your branch to merge it into a different branch of your project, you no longer need to keep the local copy of your branch. We recommend you use git branch -d to delete any branches you will no longer need to use. This makes sure your local repository remains nice and tidy.
What happens if you can't checkout?
Git will not let you checkout to switch branches if you have staged or unstaged changes.
You will have a few choices on what to do:
You can combine these approaches to deal with your changes as necessary.
When you work with git, you will typically have a local repository (the copy of your project that exists on your personal device) and a remote repository (the copy of your project that exists on the internet, usually on a service like GitHub or BitBucket)
An absolutely core part of using Git is managing the interactions between your local repository and the associated remote repository. The two key commands you will need to learn are git push (which you can use to push commits from your local repository to the remote repository) and git pull (which you can use to pull commits from the remote repository and insert them into your own local repository). We have more info on how to use these commands appropriately below.
A common mistake that newcomers to git will make is assuming that the local repository is the same as the remote repository- when they're actually 2 separate concepts. Your commits won't appear on the remote repository until you manually push them there. If someone else pushes new changes to the remote repository, you won't see their changes on your local repository until you manually pull those changes to your device.
Most version control related work happens in a local repository(staging, committing, viewing status and logs). When you start working with others on the same project, is when remote repositories come into play. It is like a file server that you use to exchange data with others.
Local | Remote |
Are located on the computers of the team members | Are on the internet or a local network |
Contains branches, commits, tags | Contains branches, commits, tags |
All “coding work” happens only in the local repository, and needs to be made and committed locally. | After the work has been committed locally, it can be “uploaded” to the remote repository in order to share with others. |
Note: You can name a local branch the same name as the remote branch/repository, but they are NOT the same
Note: You can also have multiple remote repositories(default is origin). This allows you to have multiple different working branches for new/different features of your code.
git fetch is what you do when you want to see what everybody else has been working on. it doesn’t force you to actually merge the changes into your repository. This makes fetching a safe way to review commits before integrating them with your local repository.
https://www.git-tower.com/learn/git/ebook/en/command-line/remote-repositories/introduction/
git pull [remoteName] [branchName] | Pulls all changes/commits from the remote branch, and inserts them into your current branch More technical description: fetches from the remote branch (git fetch), and merges your current branch with commits from the remote (git merge) **You can “pull” before “pushing” to a branch to avoid unwanted merge conflicts and errors**
|
git pull origin main | fetches commits from the master branch of the origin remote (into the local origin/master branch), and then it merges origin/master into the branch you currently have selected |
git push | updates the remote branch with your staged, local commits **Always “pull” before “pushing” to a branch to avoid unwanted merge conflicts and errors** |
Make sure to PULL before every PUSH if you are collaborating with others.
Conflicts generally arise when two people have changed the same lines in a file, or if one developer deleted a file while another developer was modifying it. In these cases, Git cannot automatically determine what is correct. Git will mark the file as being conflicted and halt the merging process. It is then the developers' responsibility to resolve the conflict.
Resolving merge conflicts is a completely normal part of working collaboratively
The general steps for resolving merge conflicts can be seen as:
Some useful commands for attempting to fix merge conflicts
git status | Help identify conflicted files |
git log --merge | Produces a log with a list of commits that conflict between the merging branches |
git diff | Finds the differences between the states of a repository, which helps in preventing conflicts |
git reset --mixed | Undo changes to the working directory and staging area |
In this case, there are two instances of the file “merge.text” that were modified by different branches/people. Git is unable to determine which lines to keep, as both were changed manually.
GitHub is a company that provides a service of hosting git repositories online. There are many other alternative companies that provide a similar service, like BitBucket, GitLab, and Azure DevOps, but GitHub is the most popular one and we recommend using it in this club.
The instructions for the rest of this section will focus on GitHub's features. However, almost every feature described here has equivalents in the other git hosts, so if you know how to use one you generally know how to use them all.
Pull requests
Video intro to pull requests:
https://www.youtube.com/watch?v=2VX1ISk9XH8&ab_channel=GitKraken
A pull request is a way of merging code from one branch of your remote repository into another.
You are requesting that the base branch pulls the commits from the compare branch. In the above example, you are requesting that the main branch pulls the commits from the addLaunchEvent.
We recommend you use pull requests extensively as part of your git workflow.
We encourage teams to use small, frequent, single-feature PRs. Each PR should have a name that describes exactly what the PR accomplishes. By doing smaller PRs, you will make sure everyone frequently updates their codebase so you don't end up with massive merge conflicts. By limiting your PR to a single feature, you also make it super easy to roll back that feature and reverse all commits in the PR by reverting the PR itself.
Sometimes, when you create a pull request, it will say there is a merge conflict. If this happens, don't force the PR to merge! Instead, you'll want to resolve the merge conflict. Steps:
(ex. git checkout addLaunchEvent)
(ex. git pull origin main)
(ex. git push origin addLaunchEvent)
Advantages of pull requests:
Additional readings on pull requests:
https://product.hubspot.com/blog/git-and-github-tutorial-for-beginners
https://yangsu.github.io/pull-request-tutorial/
Pull Request Reviews
One of the main advantages of pull requests is that they enable you to do a pull request review, ensuring that code that gets pulled into your primary branches has been reviewed by your team to make sure it won't introduce code smells or bugs.
PRs provide the opportunity to review another developer's code and make sure that it meets the guidelines or practices that your organization or team has. For example, if you have a developer who is more familiar with the architecture of the software system, they can provide valuable input towards making sure that your changes fit within the long term architectural vision of the system. Alternatively, you can have a newer team member who is not yet familiar with the overall code structure and they can add comments to specific parts of the code in a PR to ask for further clarification for why a certain change was made. PRs have been an essential learning tool for me during the past 10 months of my internship.
Aside from learning, PRs generally serve as a major communication channel for developers in industry, because they provide the opportunity for automated testing and improvements before your code changes are moved to the next deployment stages. One example of automated testing is using linter which is a static code analysis tool used to flag errors in your code such as bugs, stylistics errors, and suspicious constructs, like for example declaring a variable twice.
Whenever someone wants to merge a pull request, you should require them to get their PR reviewed first. To review a pull request, look at all the changes they made.
Best Practices for PR Contributors:
Best Practices for Reviewers
GitHub and other git hosts support adding inline comments, so you can comment on specific areas of the code when necessary. The best place to do this is the "Files Changed" tab of a pull request.
It is up to them to address every issue that is brought up, and push the changes back to their branch. They should let you know when they've completed everything, and you can check to make sure you're happy with their changes.
Issues
GitHub Issues are a great way of keeping track of tasks, bugs, and goals for your projects. We recommend using them as the primary way of assigning tasks to your team and planning out your dev work.
You can read more about issues here:
https://guides.github.com/features/issues/
TODO: Describe how a team of developers should use branches to do versioning correctly
TODO: Describe possible failures that a developer may encounter in this process because of mistakes they made, and how to recover from them
Let's assume you have some commits on branch yourLocalBranch, and you want to merge them into a branch on your team's GitHub (which uses the default remote alias, origin) called branchYouWantToMergeTo.
Part 1 - Set up your branch
Part 2 - Make your commits
Part 3 - Push your commits to origin
Clean up
Now that you understand the complete process on an individual level, let's take a step back to understand how your team will be using git.
Here is the Git workflow we recommend:
(This is what is used at Microsoft. It works well and it's good practice to teach it)
List of questions that Joel got often:
Refer to undoing changes and commits
Refer to undoing changes and commits
Refer to undoing changes and commits
https://www.git-tower.com/learn/git/faq/checkout-remote-branch/
Advanced Section
Here are some advanced git commands you can use to boost your git game to the next level. They are not essential to using git, but you may find them helpful. If you're still learning the beginner commands, we recommend focusing on them until you're comfortable with them before worrying about these advanced commands.
Here are some additional commands and flags for existing commands that you can use while you are staging files and adding commits.
If you want descriptions of the basic staging and commits, please see staging files & creating commits in the beginner part of the guide.
git status (-s) (-v) | You can use the -s and -v flags on git status for the following effects:
|
git add [fileName or folderName] (-u) | You can use the -u flag on git add for the following effects:
|
git commit (-a) (-am) "[Commit message here]" | You can use the -a and -am flags on git commit for the following effects:
|
https://www.atlassian.com/git/tutorials/saving-changes/git-stash
WIP, should include push, pop, apply, list
WIP, should include push, pop, apply, list
== didnt realize we already have git clean there, but we can do more detail here ==
WIP
Below is the general sequence of commands to check, and undo previous commits. Notice that we must use the commit comments as the easiest factor in differentiating between commits. It is important to use a descriptive comment for each commit.
git log | Displays old commits with the ID hash and commit comment on the current branch |
git checkout [id hash] | Will make your working directory match the exact state of the id’ed commit *nothing you do here will be saved to the current state of the project (to go back do “git checkout main”) |
git clean (-f) (-n) | ‘git clean -n’ shows which UNTRACKED files will be removed, should you do ‘git clean -f’ ** good practice is to always -n before you -f **
|
git revert | Undos a single commit |
git reset [id hash] | Goes back to specified commit by removing all subsequent commits |
WIP
git merge [branchName] | Merges the specified branch into the branch that your local directory is currently on. In a typical workflow, you will not need to use this command ever. Instead, git pull and pull requests will handle all merging for you. |
[a]Before thursday:
I will do touchups to all the beginner sections
Could you include an example in the merge conflicts section of how to solve a merge conflict?
Then let's add a little more for git stash because we might need push and pop when people inevitably make mistakes
Tech Start's API Guide |
APIs are a ubiquitous component of software engineering. APIs allow different components of a software system to interact with each other in a logical way.
Learning about APIs is critical to your growth as a software developer. This guide contains links and advice to get you started!
What is an API?
API stands for Application Programming Interface, and in simple words, allows two applications to talk to each other and send information between the two.
Further reading on the basics of APIs: https://www.plektonlabs.com/api-101-what-are-they-and-how-do-they-work/?gclid=Cj0KCQiAhf2MBhDNARIsAKXU5GRbLqWDWBPN0Zh4ZX6KwjevURl9KmQo0EVBzLn5mcePxaI_l1oWQSQaAkGDEALw_wcB
Analogy of an API
Imagine you are sitting in a restaurant with a menu and you are trying to decide what to order. You are one of the applications and in order to get food, the kitchen will act like the other application. It is the system that will “make” you food. The waiter in this scenario will be the API, and he/she delivers the food from one application(the kitchen) to another(you). The waiter is the messenger that takes your request or order and tells the kitchen – the system – what to do. Then the waiter delivers the response back to you; in this case, it is the food.
How API’s Work
Why would you need an API?
Many companies have APIs built to allow others to build interesting applications using their company data. APIs also allows a project to be dynamic - it will update the frontend information automatically when the back end is updated. This saves the hassle of going through tons of HTML code updating data one by one.
GraphQL vs Rest
Reading In favor of GraphQl:
https://www.howtographql.com/basics/1-graphql-is-the-better-rest/
Reading In favor of Rest:
https://www.rubrik.com/blog/technology/19/11/graphql-vs-rest-apis
About
https://www.ibm.com/cloud/learn/rest-apis
A REST API is an API that conforms to the design principles of the REST, or representational state transfer architectural style. For this reason, REST APIs are sometimes referred to RESTful APIs.
What is a RESTful API? https://www.youtube.com/watch?v=y0U-ZxgLu98
Types of API calls
Interactive Resource on APIs
https://apiary.io/how-to-build-api#phase-design
Tons of help on creating API with different languages https://rapidapi.com/blog/20-tutorials-on-how-to-create-your-own-api-sorted-by-programming-language/
Explanations of API’s and more in depth language-specific resources
https://www.moesif.com/blog/api-guide/getting-started-with-apis/
What is Postman?
Postman is a platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs faster.
Getting Started with Postman: https://www.guru99.com/postman-tutorial.html#1
Good Postman Series starting with setting up: https://www.youtube.com/watch?v=juldrxDrSH0&ab_channel=AutomationStepbyStep
Collection of Free, premade API’s
*Most premade API’s will have documentation of how to use/maintain them*
https://github.com/public-apis/public-apis
Example of using a premade API
https://rapidapi.com/blog/how-to-use-an-api/
Further Help
GraphQL Tutorial: https://www.youtube.com/watch?v=ed8SzALpx1Q&ab_channel=freeCodeCamp.org
What is GraphQL (A really good article): https://www.redhat.com/en/topics/api/what-is-graphql
Why use GraphQL: https://www.apollographql.com/docs/intro/benefits/
Getting started with GraphQL: https://www.apollographql.com/docs/intro/benefits/
GraphQL is split into two main parts, A Schema (Basically a model for the response), and a resolver (a collection of functions that generate response for a GraphQL query. In simple terms, a resolver acts as a GraphQL query handler)
An article that explains what a Query, Mutation & Subscription are: https://medium.com/software-insight/graphql-queries-mutations-and-subscriptions-286522b263d9
\ No newline at end of file diff --git a/guides/GraphQL_API_Guide/images/image1.png b/guides/GraphQL_API_Guide/images/image1.png new file mode 100644 index 0000000..76be96f Binary files /dev/null and b/guides/GraphQL_API_Guide/images/image1.png differ diff --git a/guides/GraphQL_API_Guide/images/image2.png b/guides/GraphQL_API_Guide/images/image2.png new file mode 100644 index 0000000..b2ba6f9 Binary files /dev/null and b/guides/GraphQL_API_Guide/images/image2.png differ diff --git a/guides/GraphQL_API_Guide/images/image3.png b/guides/GraphQL_API_Guide/images/image3.png new file mode 100644 index 0000000..381060e Binary files /dev/null and b/guides/GraphQL_API_Guide/images/image3.png differ diff --git a/guides/React_Guide/ReactGuide.html b/guides/React_Guide/ReactGuide.html new file mode 100644 index 0000000..66981a9 --- /dev/null +++ b/guides/React_Guide/ReactGuide.html @@ -0,0 +1 @@ +Tech Start's React Guide |
~~ WIP ~~
Check out this video as a crash course to React:
https://www.youtube.com/watch?v=Ke90Tje7VS0
If you find this video confusing, or prefer a different one as a React intro, please let us know :)
If you prefer reading to watching videos, this guide is helpful:
https://www.freecodecamp.org/news/the-react-handbook-b71c27b0a795/
Note on functional versus class-based components
When React was first created, class-based components were the standard. But functional components were introduced later, and they eclipse class-based components in every way.
Our advice: You should probably never use class-based components!
Stick to functional components. They are more modern and more versatile.
Lots of tutorial content online still uses class-based components. If you stumble upon a tutorial or explanation that uses a class-based component, and you're new to React, please search for a functional-component alternative instead!
This section of the guide contains some topics you'll want to learn once you know the essentials of React.
React Hooks
In general, a good order of learning hooks is:
Other built-in hooks exist, but their uses are more niche, and should only be learned when necessary.
Creating custom hooks
You can, and should, create custom hooks in React. Using custom hooks, you can encapsulate a bunch of complicated logic into a simple hook function call, and name it appropriately to describe what your custom hook accomplishes.
https://www.youtube.com/watch?v=Jl4q2cccwf0&ab_channel=TheNetNinja
Calling APIs - Part 1
Calling APIs is a key part of any React App. It's what enables your app to communicate with the outside world - including, presumably, your backend.
Here's a helpful tutorial on the best practices for calling APIs in React:
https://www.youtube.com/watch?v=bYFYF2GnMy8
There's also a part 2:
https://www.youtube.com/watch?v=1tfd6ANaNRY
The best ways to fetch data are using the popular package Axos (https://www.npmjs.com/package/axios) or the vanilla JS fetch function (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
Once you have parsed the data, you'll probably want to store it in your state somehow (using useState or a redux store).
It is also good practice to encapsulate your entire API call into a custom hook, and name the hook according to what it does (ex. useFetchPokemonStats). This is also much easier to do if you use a state-management system like Redux, which is described below.
Calling APIs - Part 2
The methods described above let you call an API immediately upon rendering a certain component. But what happens if you want to manually trigger an API call (ex. after a certain action or event)?
Your API call should still use useEffect, and should look mostly like the calls you learned about in Part 1. The 1 difference is you need to guard the useEffect wisely in its dependency array.
As you recall, the dependency array of a useEffect contains everything that the useEffect depends upon - if any of its dependencies change, it will have a side effect of rerunning the useEffect.
So, you can define a specific variable which only changes when you want your API call to run. You can put that variable in the dependency array of the useEffect. When the action or event that you want to trigger the API call occurs, you should change the trigger variable. This change will trigger the useEffect to run. The trigger variable should never change except when you want the useEffect to run.
I personally like making my trigger variable an object which also contains any subvariables that my API call needs. So for example, if I was coding a call to a search API that included a text query and target price, my trigger object would contain those.
Here is an example of a very basic React application that calls an API to perform a search with a custom hook and a useEffect guarded by a trigger object as described above: https://github.com/Tech-Start-UCalgary/react-api-examples/tree/main/js-no-redux
useSWR
useSWR is a useful React hook for data fetching published by Vercel (creators of Next.js).
SWR stands for stale-while-revalidate. It allows for smart data fetching and encapsulates a lot of advanced fetching logic (like how often should you refetch? should you have a cache?) in a single line of code.
You should read more about useSWR here:
You can watch a video tutorial here:
https://www.youtube.com/watch?v=f7yjEdXgGiM
Redux is a widely used state container for javascript apps. As soon as your app reaches any level of data complexity, it makes a ton of sense to start using Redux to manage your state.
A fair warning: Redux will seem complicated at the beginning. That's completely expected! Push through that initial discomfort and you'll get used to it in no time :)
Here is a great introductory video to React-Redux:
https://www.youtube.com/watch?v=CVpUuw9XSjY
I highly recommend using createSlice to setup your Redux Store. It is a simple way to encapsulate creating actions and slicers in a simple, easy-to-read, easy-to-understand way. Here is a short video that does a great job explaining how to use createSlice:
https://www.youtube.com/watch?v=e0MEtFaQTZk
To access your Redux state and update your Redux state in React, I highly recommend using the twin hooks useSelector and useDispatch respectively. They are simple, easy, and elegant.
https://www.youtube.com/watch?v=3zoIigieur0
~~ Joel will add a full example Redux App here eventually ~~
Legend
General[a]
Setup
First Project
JavaScript Basics
Variable
Arrow Functions
Classes[b]
Callbacks
Promises
Await/Async[c]
React Basics
Components
Props vs State
Events
Forms in React
Higher Order Components
Hooks
React Ecosystem
React Router
Redux[f]
Next.js
Setup
Prerequisites
In order to use create-react-app, you need to have Node.js installed. Node includes npm (the node package manager), and npx (the node package runner).
You should also have a code editor(like VS Code) installed.
First Project - Creating create-react-app
Make sure you cd to the place you'd like your app to live on your hard drive, then run the following in your terminal:
npx create-react-app name-of-your-app
Note: npx create-react-app will download some Node_Modules for you, but if you are getting a react project that is already made, you will need to type npm install in your terminal.
JavaScript Basics
Variables
Chakra UI is a great frontend ui framework, and it works well with React. Here's a tutorial series that teaches you the basics quickly:
https://egghead.io/courses/build-a-modern-user-interface-with-chakra-ui-fac68106
[a]@terryfu33@gmail.com
In general, this guide should have minimal in-guide
1, max 2 sentences explaining what a topic is, and why it should be learned, and then a link to a single good video/playlist which explains it. Good formatting is necessary too.
Should be a springboard to explanations, not an attempt to re-explain things
[b]Classes wont be necessary for React
[c]Async js is super helpful so it's good to have it here! We may want to include it at end instead of beginning though. Check if I added stuff to our web dev guide too
[d]The React Basics section can just be condensed to a single good video/playlist which teaches the essentials
[e]I think I covered almost all this in the intro section I added
[f]I got this
Tech Start's Super Awesome Mega Web Dev Guide :) |
Want to get into web dev? This guide should be your best friend. It covers some of the basic tools and languages you'll need to dive into the world of web development. Rather than providing tutorials, this guide focuses on directing you towards the best free online content to teach you the fundamentals.
Our advice:
This guide was originally developed for Tech Start UCalgary, an undergraduate club for software development and entrepreneurship at the University of Calgary. Check us out here -----> https://linktr.ee/techstartuofc <-----
Or at our website here ---> https://techstartucalgary.com <-----
If you're interested in contributing to this guide or in building similar guides for other areas (like Git, project management, or mobile development), please send me an email at joel.happ1@ucalgary.ca
Good luck on your web dev journey and enjoy the guide!
HTML is the building block of all webpages. It's ubiquitous throughout application design - everything from websites to mobile apps to desktop apps use HTML as the base for their frontend.
Luckily for you, HTML is also very simple. Don't spend too much time learning it as a beginner - while more advanced HTML concepts do exist, all you need to get started is a basic understanding of how it works.
To get started, I recommend watching this video and experimenting by building your own HTML document alongside it: https://www.youtube.com/watch?v=UB1O30fR-EE
To generate a favicon for your site, use this site: https://realfavicongenerator.net/
No matter which text editor you use (Atom, VSCode, Sublime Text, or something else), I recommend searching for editor plugins to improve your experience writing HTML. Google is your friend here.
If HTML is the backbone of frontends, CSS is the paint. It's an extremely versatile and powerful way of styling and beautifying your content. CSS is easy to get into, but very difficult to master- if front-end development interests you, I recommend dedicating a significant amount of time towards learning CSS.
To get started, check out this 1 hour introductory video to CSS:
https://www.youtube.com/watch?v=yfoY53QXEnI
Another alternative is this free course on Scrimba, which also covers HTML: https://scrimba.com/learn/htmlcss
One topic that isn't covered in much detail in the previous video is CSS selectors. For most purposes, you'll probably want to keep your selectors simple (using only class names the vast majority of the time). If you want to learn more about advanced selectors, check out this video: https://www.youtube.com/watch?v=Bcr70LIJcOk
When working on projects with other people, I recommend avoiding element-level selectors, as they can easily screw with work that others are doing.
Once you have a good grasp of the basics of CSS, one area that I highly recommend learning is CSS Grid. CSS Grid is a fantastic way of organizing and positioning html content, and it comes with loads of features that make it easy to build adaptive web pages. By combining CSS Grid with CSS breakpoints, you can design interfaces that look great on any size of screen, whether it's a big desktop computer or a small mobile phone.
Highly recommended course for CSS Grid: https://scrimba.com/learn/cssgrid
(Warning: CSS grid is not supported by some outdated browsers and browsers like Opera Mini which are used mostly in third world countries to save data. If your intended audience includes these people, do not use CSS Grid.)
How should you organize and refactor your CSS? I recommend using the BEM strategy for your CSS selectors. https://css-tricks.com/bem-101/
BEM is a particularly helpful methodology to adopt when you're building websites from components like in React, since you can keep the CSS for each component in a separate file. However, it's far from the only strategy for CSS organization, so you can research and adopt others if they suit your needs better.
Knowing CSS Flexbox is also helpful for arranging the layouts of websites. It's like
Finally, here is a great video to watch for some simple, powerful tips for writing modern CSS:
https://www.youtube.com/watch?v=Qhaz36TZG5Y
JavaScript drives the interactivity and functionality of websites. Using it, you can respond to the user's interactions on your website and you can modify the properties of your HTML dynamically.
To get started with JavaScript, you can watch this playlist: https://www.youtube.com/playlist?list=PLDyQo7g0_nsX8_gZAB8KD1lL4j4halQBJ
If you're brand new to programming, you should spend more time learning the fundamentals of JavaScript.
There are a few areas of JavaScript which may be confusing to any newcomers to the language, even if you have previously learned other languages. I recommend practicing and studying to get used to these features.
Array functions: https://www.youtube.com/watch?v=rRgD1yVwIvE
Asynchronous JavaScript (Await, Promises, Callbacks): https://www.youtube.com/watch?v=_8gHHBlbziw
JSON (JavaScript object notation): https://www.youtube.com/watch?v=wI1CWzNtE-M
If you encounter another area of JavaScript that gives you troubles let me know and I'll add a section to this guide.
Here's an introduction to Node.js: JavaScript, but for your operating system instead of your browser. https://www.youtube.com/watch?v=ENrzD9HAZK4 Node.js is frequently used as the backend for the server side of an application or web app.
Installing Node.js
We recommend using NVM (node version manager) to install node.js. It allows you to install, uninstall, and switch between different versions of node.js very quickly. Often, you'll need to switch between different versions of node for different projects, so you'll save time by using nvm from the start.
NVM (Mac/Linux): https://github.com/nvm-sh/nvm
NVM for windows: https://github.com/coreybutler/nvm-windows
Once installed, you can type "nvm" in your CLI to see a list of commands you can use. Follow instructions on GitHub for proper usage!
Express
One of the most common Node.js frameworks is Express. Express is an unopinionated (meaning, it doesn't force you to do things any particular way) framework that excels at allowing you to write APIs. We may include more content on Express here in the future.
TypeScript is a superset of Javascript that introduces new features mostly related to strong typing: this lets you safeguard your variables and functions and helps you catch the most frequent mistakes and bugs from JavaScript code.
TypeScript is contained in .ts files, and using a compiler you can compile it into vanilla JavaScript .js files which run as expected on a computer. If you anticipate doing a lot of work with JavaScript, I highly recommend learning TypeScript - although it isn't necessary for every project, it immensely improves the quality of the coding experience and it can guide you towards mastering tricky concepts from JavaScript like promises and callbacks.
TypeScript tutorial by NetNinja:
https://www.youtube.com/playlist?list=PL4cUxeGkcC9gUgr39Q_yD6v-bSyMwKPUI
TypeScript Basics by JavaBrains: (also includes small project incl. API usage)
https://www.youtube.com/playlist?list=PLqq-6Pq4lTTanfgsbnFzfWUhhAz3tIezU
HTML, CSS, and JS are great and all, but what if you want to make a modern, reactive website? Several frontend frameworks exist to empower dynamic, reactive sites, and as of 2021 React is by far the most popular among them. It's a great way to get an introduction into the world of web frameworks and it's a fun tool to use.
Here's a good free overview of the basics of React: https://scrimba.com/learn/learnreact
Plenty of similar courses are available on YouTube and other course platforms.
When in doubt, consult the React documentation: https://reactjs.org/docs/
Note that much of the documentation still uses class-based components, as opposed to function-based components. Nowadays, it is 99% preferable to use function-based components whenever possible. It cuts down on the amount of boilerplate you need to write and enables helpful features from hooks.
Learn more about React Hooks: https://www.youtube.com/watch?v=TNhaISOUy6Q
The 2 hooks in particular you should be very familiar with:
Other hooks that are handy to know about are useRef, useCallback, and useLayoutEffect.
To learn React, you should know HTML, CSS, and JS before. You can also build React apps with TypeScript instead of JavaScript. https://www.typescriptlang.org/docs/handbook/react.html
React Context API
The context API is a tool you can use in React to share state from an upper level component (a provider) to its children. If you have state that needs to be shared across many children, you can use the context API rather than pass everything down manually as props.
To learn about the context API, I recommend watching this video:
https://www.youtube.com/watch?v=35lXWvCuM8o
React Router
If you want to make React websites with multiple pages, you can use React Router. It is a package that enables your app to have multiple routes. This series gives a quick overview of React Router:
https://www.youtube.com/watch?v=aZGzwEjZrXc&list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d&index=21
React Router is lightweight and best suited for very small projects. If you plan on building a larger website with React, we recommend using Next.js instead - it has a lot more features including pre-rendering pages on the server for SEO optimization. Next.js is also widely used and has industry-level support.
React Redux is a commonly used package for managing global state. It allows you to take care of state in a global store that can be accessed or modified from anywhere in your application.
https://www.youtube.com/watch?v=CVpUuw9XSjY
Using React with Typescript
Here is a great cheatsheet that you should read to get started with using React in combination with Typescript:
https://github.com/typescript-cheatsheets/react
That's it! .. for now :)
Happy hacking!
\ No newline at end of file diff --git a/guides/Web_Dev_Guide/images/image1.png b/guides/Web_Dev_Guide/images/image1.png new file mode 100644 index 0000000..3efb287 Binary files /dev/null and b/guides/Web_Dev_Guide/images/image1.png differ diff --git a/guides/Web_Dev_Guide/images/image2.png b/guides/Web_Dev_Guide/images/image2.png new file mode 100644 index 0000000..2793bfa Binary files /dev/null and b/guides/Web_Dev_Guide/images/image2.png differ diff --git a/guides/Web_Dev_Guide/images/image3.png b/guides/Web_Dev_Guide/images/image3.png new file mode 100644 index 0000000..53c2d49 Binary files /dev/null and b/guides/Web_Dev_Guide/images/image3.png differ diff --git a/guides/Web_Dev_Guide/images/image4.png b/guides/Web_Dev_Guide/images/image4.png new file mode 100644 index 0000000..2b86ff9 Binary files /dev/null and b/guides/Web_Dev_Guide/images/image4.png differ diff --git a/guides/Web_Dev_Guide/images/image5.png b/guides/Web_Dev_Guide/images/image5.png new file mode 100644 index 0000000..23087c6 Binary files /dev/null and b/guides/Web_Dev_Guide/images/image5.png differ diff --git a/guides/Web_Dev_Guide/images/image6.png b/guides/Web_Dev_Guide/images/image6.png new file mode 100644 index 0000000..8453ca8 Binary files /dev/null and b/guides/Web_Dev_Guide/images/image6.png differ diff --git a/guides/Web_Dev_Guide/images/image7.png b/guides/Web_Dev_Guide/images/image7.png new file mode 100644 index 0000000..677009d Binary files /dev/null and b/guides/Web_Dev_Guide/images/image7.png differ diff --git a/guides/Web_Dev_Guide/images/image8.png b/guides/Web_Dev_Guide/images/image8.png new file mode 100644 index 0000000..48a0360 Binary files /dev/null and b/guides/Web_Dev_Guide/images/image8.png differ diff --git a/guides/html2Markdown.py b/guides/html2Markdown.py new file mode 100644 index 0000000..b7218d9 --- /dev/null +++ b/guides/html2Markdown.py @@ -0,0 +1,20 @@ + +# import markdownify +import markdownify + + +def get_content(path): + with open(path) as f: + raw = f.read() + return raw + +html = get_content('./Django_Guide/DjangoGuide.html') +print(html) + +# convert html to markdown +h = markdownify.markdownify(html, heading_style="ATX", code_language="python", heading_style_level=2) + +with open('./Django_Guide/DjangoGuide.md', "w", encoding="utf-8") as f: + f.write(h) +f.close() + diff --git a/highlight.css b/highlight.css new file mode 100644 index 0000000..ba57b82 --- /dev/null +++ b/highlight.css @@ -0,0 +1,82 @@ +/* + * An increased contrast highlighting scheme loosely based on the + * "Base16 Atelier Dune Light" theme by Bram de Haan + * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) + * Original Base16 color scheme by Chris Kempson + * (https://github.com/chriskempson/base16) + */ + +/* Comment */ +.hljs-comment, +.hljs-quote { + color: #575757; +} + +/* Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-tag, +.hljs-name, +.hljs-regexp, +.hljs-link, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class { + color: #d70025; +} + +/* Orange */ +.hljs-number, +.hljs-meta, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #b21e00; +} + +/* Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet { + color: #008200; +} + +/* Blue */ +.hljs-title, +.hljs-section { + color: #0030f2; +} + +/* Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #9d00ec; +} + +.hljs { + display: block; + overflow-x: auto; + background: #f6f7f6; + color: #000; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #22863a; + background-color: #f0fff4; +} + +.hljs-deletion { + color: #b31d28; + background-color: #ffeef0; +} diff --git a/highlight.js b/highlight.js new file mode 100644 index 0000000..180385b --- /dev/null +++ b/highlight.js @@ -0,0 +1,6 @@ +/* + Highlight.js 10.1.1 (93fd0d73) + License: BSD-3-Clause + Copyright (c) 2006-2020, Ivan Sagalaev +*/ +var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offsetHi there! +This website contains tutorials, how-to guides, explanations, +and reference documentation of the things we do at +Tech Start UCalgary.
+If you are looking for our main page, +please visit us at techstartucalgary.com.
+ +