diff --git a/championship/factories.py b/championship/factories.py
index 3d21907083c6d3711aba7a132a6e57f865ba16c1..37a541512e8eff16dbdcc95b596379f3fd11b386 100644
--- a/championship/factories.py
+++ b/championship/factories.py
@@ -26,14 +26,49 @@ class UserFactory(DjangoModelFactory):
     password = factory.LazyFunction(lambda: make_password("foobar"))
 
 
+class AddressFactory(DjangoModelFactory):
+    class Meta:
+        model = Address
+
+    location_name = factory.Faker("company", locale="fr_CH")
+    street_address = factory.Faker("street_address", locale="fr_CH")
+    city = factory.Faker("city", locale="fr_CH")
+    postal_code = factory.Faker("postcode", locale="fr_CH")
+    region = factory.Faker(
+        "random_element",
+        elements=Address.Region.values,
+    )
+    country = factory.Faker(
+        "random_element",
+        elements=Address.Country.values,
+    )
+
+
 class EventOrganizerFactory(DjangoModelFactory):
     class Meta:
         model = EventOrganizer
 
     name = factory.Faker("company", locale="fr_CH")
     contact = factory.Faker("email")
+    description = factory.Faker("text")
     user = factory.SubFactory(UserFactory)
 
+    @factory.post_generation
+    def addresses(self, create, extracted, **kwargs):
+        if not create:
+            # Simple build, do nothing.
+            return
+
+        if extracted == None:
+            # Create 3 new random addresses.
+            addresses = AddressFactory.create_batch(3, organizer=self)
+            for address in addresses:
+                self.addresses.add(address)
+
+            # Set one as the default
+            self.default_address = addresses[0]
+            self.save()
+
 
 class PlayerFactory(DjangoModelFactory):
     class Meta:
diff --git a/championship/forms.py b/championship/forms.py
index 7c7ea093d6a074591195dfe837404eaf6597cb45..96f8c1ce40eb32bb23a841dad455429a3edfb8de 100644
--- a/championship/forms.py
+++ b/championship/forms.py
@@ -1,7 +1,7 @@
 from django.db.models import TextChoices, Count
 from django.core.validators import RegexValidator
 from django import forms
-from .models import Event, EventPlayerResult
+from .models import Address, Event, EventPlayerResult, EventOrganizer
 from crispy_forms.helper import FormHelper
 from crispy_forms.layout import Submit, Layout, Div, Field
 from tinymce.widgets import TinyMCE
@@ -23,6 +23,7 @@ class EventCreateForm(forms.ModelForm):
             "date",
             "format",
             "category",
+            "address",
             "url",
             "decklists_url",
             "description",
@@ -44,6 +45,57 @@ You can copy/paste the description from a website like swissmtg.ch, and the form
             "format": "If your desired format is not listed, please contact us and we'll add it.",
         }
 
+    def __init__(self, *args, **kwargs):
+        organizer = kwargs.pop("organizer", None)
+        super(EventCreateForm, self).__init__(*args, **kwargs)
+        if organizer is not None:
+            self.fields["address"].queryset = organizer.get_addresses()
+
+
+class AddressForm(forms.ModelForm):
+    set_as_organizer_address = forms.BooleanField(required=False, initial=False)
+
+    class Meta:
+        model = Address
+        fields = [
+            "location_name",
+            "street_address",
+            "city",
+            "postal_code",
+            "region",
+            "country",
+            "set_as_organizer_address",
+        ]
+
+
+class OrganizerProfileEditForm(forms.ModelForm):
+    class Meta:
+        model = EventOrganizer
+        fields = [
+            "name",
+            "contact",
+            "default_address",
+            "description",
+        ]
+        widgets = {
+            "description": TinyMCE(
+                mce_attrs={
+                    "toolbar": "undo redo | bold italic | link unlink | bullist numlist",
+                    "link_assume_external_targets": "http",
+                },
+            ),
+        }
+        help_texts = {
+            "description": """Supports the following HTML tags: {}.""".format(
+                ", ".join(bleach.ALLOWED_TAGS)
+            ),
+        }
+
+    def __init__(self, *args, **kwargs):
+        organizer = kwargs.get("instance", None)
+        super().__init__(*args, **kwargs)
+        self.fields["default_address"].queryset = organizer.get_addresses()
+
 
 class LinkImporterForm(forms.Form, SubmitButtonMixin):
     url = forms.URLField(
diff --git a/championship/migrations/0016_eventorganizer_description_and_more.py b/championship/migrations/0016_eventorganizer_description_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f512efd1f486636e58c730bd0cd225c45f4d6a5
--- /dev/null
+++ b/championship/migrations/0016_eventorganizer_description_and_more.py
@@ -0,0 +1,127 @@
+# Generated by Django 4.1.7 on 2023-06-30 21:52
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django_bleach.models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("championship", "0015_player_email_alter_event_description"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="eventorganizer",
+            name="description",
+            field=django_bleach.models.BleachField(
+                blank=True,
+                help_text="Supports the following HTML tags: a, b, blockquote, em, i, li, ol, p, strong, ul",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="eventorganizer",
+            name="contact",
+            field=models.EmailField(
+                help_text="Prefered contact email (not visible to players)",
+                max_length=254,
+            ),
+        ),
+        migrations.CreateModel(
+            name="Address",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("location_name", models.CharField(max_length=255)),
+                ("street_address", models.CharField(max_length=255)),
+                ("city", models.CharField(max_length=255)),
+                ("postal_code", models.CharField(max_length=10)),
+                (
+                    "region",
+                    models.CharField(
+                        choices=[
+                            ("AG", "Aargau"),
+                            ("AR", "Appenzell Ausserrhoden"),
+                            ("AI", "Appenzell Innerrhoden"),
+                            ("BL", "Basel-Landschaft"),
+                            ("BS", "Basel-Stadt"),
+                            ("BE", "Bern"),
+                            ("FR", "Fribourg"),
+                            ("GE", "Genève"),
+                            ("GL", "Glarus"),
+                            ("GR", "Graubünden"),
+                            ("JU", "Jura"),
+                            ("LU", "Luzern"),
+                            ("NE", "Neuchâtel"),
+                            ("NW", "Nidwalden"),
+                            ("OW", "Obwalden"),
+                            ("SH", "Schaffhausen"),
+                            ("SZ", "Schwyz"),
+                            ("SO", "Solothurn"),
+                            ("SG", "Sankt Gallen"),
+                            ("TG", "Thurgau"),
+                            ("TI", "Ticino"),
+                            ("UR", "Uri"),
+                            ("VS", "Valais"),
+                            ("VD", "Vaud"),
+                            ("ZG", "Zug"),
+                            ("ZH", "Zürich"),
+                            ("FR_DE", "Freiburg im Breisgau (DE)"),
+                        ],
+                        default="ZH",
+                        max_length=5,
+                    ),
+                ),
+                (
+                    "country",
+                    models.CharField(
+                        choices=[
+                            ("CH", "Switzerland"),
+                            ("AT", "Austria"),
+                            ("DE", "Germany"),
+                            ("IT", "Italy"),
+                            ("LI", "Liechtenstein"),
+                            ("FR", "France"),
+                        ],
+                        default="CH",
+                        max_length=2,
+                    ),
+                ),
+                (
+                    "organizer",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="addresses",
+                        to="championship.eventorganizer",
+                    ),
+                ),
+            ],
+        ),
+        migrations.AddField(
+            model_name="event",
+            name="address",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                to="championship.address",
+            ),
+        ),
+        migrations.AddField(
+            model_name="eventorganizer",
+            name="default_address",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                to="championship.address",
+            ),
+        ),
+    ]
diff --git a/championship/models.py b/championship/models.py
index 70fef93b216f06e72be94337da9b32453ef598e7..3c2a596b883de3db7bad805f3120441f8de7e1f0 100644
--- a/championship/models.py
+++ b/championship/models.py
@@ -11,6 +11,91 @@ import collections
 import datetime
 from prometheus_client import Gauge, Summary
 from django.contrib.humanize.templatetags.humanize import ordinal
+import urllib.parse
+from django.contrib.auth.models import User
+
+
+class Address(models.Model):
+    class Region(models.TextChoices):
+        AARGAU = "AG", "Aargau"  # German
+        APPENZELL_AUSSERRHODEN = "AR", "Appenzell Ausserrhoden"  # German
+        APPENZELL_INNERRHODEN = "AI", "Appenzell Innerrhoden"  # German
+        BASEL_LANDSCHAFT = "BL", "Basel-Landschaft"  # German
+        BASEL_STADT = "BS", "Basel-Stadt"  # German
+        BERN = "BE", "Bern"  # German
+        FRIBOURG = "FR", "Fribourg"  # French
+        GENEVA = "GE", "Genève"  # French
+        GLARUS = "GL", "Glarus"  # German
+        GRAUBUNDEN = "GR", "Graubünden"  # German
+        JURA = "JU", "Jura"  # French
+        LUCERNE = "LU", "Luzern"  # German
+        NEUCHATEL = "NE", "Neuchâtel"  # French
+        NIDWALDEN = "NW", "Nidwalden"  # German
+        OBWALDEN = "OW", "Obwalden"  # German
+        SCHAFFHAUSEN = "SH", "Schaffhausen"  # German
+        SCHWYZ = "SZ", "Schwyz"  # German
+        SOLOTHURN = "SO", "Solothurn"  # German
+        ST_GALLEN = "SG", "Sankt Gallen"  # German
+        THURGAU = "TG", "Thurgau"  # German
+        TICINO = "TI", "Ticino"  # Italian
+        URI = "UR", "Uri"  # German
+        VALAIS = "VS", "Valais"  # French
+        VAUD = "VD", "Vaud"  # French
+        ZUG = "ZG", "Zug"  # German
+        ZURICH = "ZH", "Zürich"  # German
+        FREIBURG_DE = "FR_DE", "Freiburg im Breisgau (DE)"  # German
+
+    class Country(models.TextChoices):
+        SWITZERLAND = "CH", "Switzerland"
+        AUSTRIA = "AT", "Austria"
+        GERMANY = "DE", "Germany"
+        ITALY = "IT", "Italy"
+        LIECHTENSTEIN = "LI", "Liechtenstein"
+        FRANCE = "FR", "France"
+
+    location_name = models.CharField(max_length=255)
+    street_address = models.CharField(max_length=255)
+    city = models.CharField(max_length=255)
+    postal_code = models.CharField(max_length=10)
+    region = models.CharField(
+        max_length=5,
+        choices=Region.choices,
+        default=Region.ZURICH,
+    )
+    country = models.CharField(
+        max_length=2, choices=Country.choices, default=Country.SWITZERLAND
+    )
+
+    organizer = models.ForeignKey(
+        "EventOrganizer", on_delete=models.CASCADE, related_name="addresses"
+    )
+
+    # Used for naming this object in the deletion popup
+    display_name = "Address"
+
+    def get_delete_url(self):
+        return reverse("address_delete", args=[self.pk])
+
+    def get_absolute_url(self):
+        return reverse("address_edit", args=[self.pk])
+
+    def __str__(self):
+        address_parts = [
+            self.location_name,
+            self.street_address,
+            self.postal_code,
+            self.city,
+        ]
+        # If the city is the same as the region, we don't need it twice
+        if self.get_region_display() != self.city:
+            address_parts.append(self.get_region_display())
+        address_parts.append(self.get_country_display())
+        return ", ".join(address_parts)
+
+    def get_google_maps_url(self):
+        """Return a URL for this address on Google Maps."""
+        query = urllib.parse.quote(self.__str__())
+        return f"https://www.google.com/maps/search/?api=1&query={query}"
 
 
 class EventOrganizer(models.Model):
@@ -19,8 +104,26 @@ class EventOrganizer(models.Model):
     """
 
     name = models.CharField(max_length=200)
-    contact = models.EmailField(help_text="Prefered contact email")
+    contact = models.EmailField(
+        help_text="Prefered contact email (not visible to players)"
+    )
+    description = BleachField(
+        help_text="Supports the following HTML tags: {}".format(
+            ", ".join(settings.BLEACH_ALLOWED_TAGS)
+        ),
+        blank=True,
+        strip_tags=True,
+    )
     user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
+    default_address = models.ForeignKey(
+        Address, on_delete=models.SET_NULL, null=True, blank=True
+    )
+
+    def get_absolute_url(self):
+        return reverse("organizer_details", args=[self.pk])
+
+    def get_addresses(self):
+        return self.addresses.all()
 
     def __str__(self):
         return self.name
@@ -68,6 +171,9 @@ class Event(models.Model):
         blank=True,
         strip_tags=True,
     )
+    address = models.ForeignKey(
+        Address, on_delete=models.SET_NULL, null=True, blank=True
+    )
 
     class Format(models.TextChoices):
         LEGACY = "LEGACY", "Legacy"
diff --git a/championship/templates/championship/address_form.html b/championship/templates/championship/address_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..e7b12b7ff67f17eb73a411c6367c9fe21e40379e
--- /dev/null
+++ b/championship/templates/championship/address_form.html
@@ -0,0 +1,17 @@
+{% extends "championship/base.html" %}
+
+{% load crispy_forms_tags %}
+
+{% block content %}
+    <h2>{% if address %}Edit{% else %}Create{% endif %} Address</h2>
+    <p>
+        <a class="btn btn-secondary-light" href="{% url 'address_list' %}">Back to Addresses</a>
+        {% include 'championship/delete_confirmation.html' with object=address %}
+    </p>
+    <form method="post">
+        {% csrf_token %}
+        {{ form|crispy }}
+        <button class="btn btn-secondary" type="submit">Save</button>
+    </form>
+
+{% endblock %}
\ No newline at end of file
diff --git a/championship/templates/championship/address_list.html b/championship/templates/championship/address_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..54c669453dcedf7478aa26ca714725995b6734ea
--- /dev/null
+++ b/championship/templates/championship/address_list.html
@@ -0,0 +1,42 @@
+{% extends "championship/base.html" %}
+
+{% block content %}
+    <div class="container">
+        <h2>Your Addresses</h2>
+        <p>
+            <a class="btn btn-secondary-light" href="{{ organizer_url }}">Back to My Events</a>
+        </p>
+        <table class="table table-striped">
+            <thead>
+                <tr>
+                    <th>Location Name</th>
+                    <th>Street Address</th>
+                    <th>City</th>
+                    <th>Postal Code</th>
+                    <th>Region</th>
+                    <th>Actions</th>
+                </tr>
+            </thead>
+            <tbody>
+                {% for address in view.get_queryset %}
+                    <tr>
+                        <td>{{ address.location_name }}</td>
+                        <td>{{ address.street_address }}</td>
+                        <td>{{ address.city }}</td>
+                        <td>{{ address.postal_code }}</td>
+                        <td>{{ address.get_region_display }}</td>
+                        <td>
+                            <a class="btn btn-secondary" href="{% url 'address_edit' address.id %}">Edit</a>
+                            {% include 'championship/delete_confirmation.html' with object=address %}
+                        </td>
+                    </tr>
+                {% empty %}
+                    <tr>
+                        <td colspan="6">No addresses found</td>
+                    </tr>
+                {% endfor %}
+            </tbody>
+        </table>
+        <a class="btn btn-secondary" href="{% url 'address_create' %}">Add New Address</a>
+    </div>
+{% endblock %}
\ No newline at end of file
diff --git a/championship/templates/championship/base.html b/championship/templates/championship/base.html
index 99a17bd088b666e3da00daef00a1540d648a1878..4f8b93d222dbcad89e70272c4abbc38ff8e1a4bf 100644
--- a/championship/templates/championship/base.html
+++ b/championship/templates/championship/base.html
@@ -47,7 +47,7 @@
                                         {% if user.eventorganizer %}
                                             <li><a class="dropdown-item" href="{% url 'events_create' %}">Create new event</a></li>
                                             <li><a class="dropdown-item" href="{% url 'results_create' %}">Upload results</a></li>
-                                            <li><a class="dropdown-item" href="{% url 'organizer_update' %}">Edit organizer profile</a></li>
+                                            <li><a class="dropdown-item" href="{% url 'organizer_details' user.eventorganizer.pk %}">My Events</a></li>
                                             <li><a class="dropdown-item" href="{% url 'invoice_list' %}">My invoices</a></li>
                                         {% endif %}
                                         {% if user.is_staff %}
diff --git a/championship/templates/championship/delete_confirmation.html b/championship/templates/championship/delete_confirmation.html
new file mode 100644
index 0000000000000000000000000000000000000000..aa1e26b3fb848c2767d3b5f2fbbb1a66ac45be0e
--- /dev/null
+++ b/championship/templates/championship/delete_confirmation.html
@@ -0,0 +1,26 @@
+{% if object %}
+    <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal{{ object.id }}">
+        Delete    </button>
+
+    <div class="modal fade" id="deleteModal{{ object.id }}">
+        <div class="modal-dialog">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">Delete {{ object.display_name }}</h5>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                </div>
+                <div class="modal-body">
+                    <h6>Are you sure you want to delete this {{ object.name|lower }}?</h6>
+                    <p>{{ object }}</p>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
+                    <form method="post" action="{{ object.get_delete_url }}">
+                        {% csrf_token %}
+                        <button class="btn btn-danger" type="submit">Delete</button>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+{% endif %}
\ No newline at end of file
diff --git a/championship/templates/championship/event_details.html b/championship/templates/championship/event_details.html
index fd361550d4d2aa6a06f54817ee3cc3d175241d53..ca4509ac4fdb9be7a30c216af96acf7ac4e476f7 100644
--- a/championship/templates/championship/event_details.html
+++ b/championship/templates/championship/event_details.html
@@ -13,6 +13,10 @@
         <dd class="col-sm-9">{{ event.organizer.name }}</dd>
         <dt class="col-sm-3">Date</dt>
         <dd class="col-sm-9">{{ event.date }}</dd>
+        {% if event.address %}
+            <dt class="col-sm-3">Location</dt>
+            <dd class="col-sm-9">{{ event.address }} (<a href="{{ event.address.get_google_maps_url }}" target="_blank">View on Google Maps</a>)</dd>
+        {% endif %}
         <dt class="col-sm-3">Category</dt>
         <dd class="col-sm-9">{{ event.get_category_display }}</dd>
         <dt class="col-sm-3">Format</dt>
diff --git a/championship/templates/championship/organizer_details.html b/championship/templates/championship/organizer_details.html
new file mode 100644
index 0000000000000000000000000000000000000000..280cae92dc49172899ab03627f71c6f8b205b2c3
--- /dev/null
+++ b/championship/templates/championship/organizer_details.html
@@ -0,0 +1,63 @@
+{% extends "championship/base.html" %}
+
+{% block title %}
+    {{ eventorganizer.name }}
+{% endblock %}
+
+{% block content %}
+    <h1>{{ eventorganizer.name }}</h1>
+    <dl class="row">
+        {% if eventorganizer.user == user %}
+            <dt class="col-sm-3">TO actions (only shown to you)</dt>
+            <dd class="col-sm-9">
+                <p><a href="{% url 'organizer_update' %}" class="btn btn-secondary">Edit Organizer</a></p>
+                <p><a href="{% url 'address_list' %}" class="btn btn-secondary">Edit Addresses</a></p>
+            </dd>
+        {% endif %}
+        {% if user.is_staff %}
+            <dt class="col-sm-3">Admin (shown only to staff users)</dt>
+            <dd class="col-sm-9">
+                <a class="btn btn-warning" href="{% url 'admin:championship_eventorganizer_change' eventorganizer.id %}">Edit in admin</a>
+            </dd>
+        {% endif %}
+        {% if eventorganizer.default_address %}
+            <dt class="col-sm-3">Region</dt>
+            <dd class="col-sm-9">{{ eventorganizer.default_address.get_region_display }}</dd>
+            <dt class="col-sm-3">Address</dt>
+            <dd class="col-sm-9">{{ eventorganizer.default_address }} (<a href="{{ eventorganizer.default_address.get_google_maps_url }}" target="_blank">View on Google Maps</a>)</dd>
+        {% endif %}
+    </dl>
+
+    {% if eventorganizer.description %}
+        <h3>About</h3>
+        <div class="event_description border rounded">
+            {{eventorganizer.description|linebreaksbr }}
+        </div>
+    {% endif %}
+
+    {% for event_type in all_events %}
+        <br>
+        <h2>{{ event_type.title }}</h2>
+        <div class="table-responsive">
+            <table class="table table-striped">
+                <thead>
+                    <tr>
+                        <th scope="col">Event</th>
+                        <th scope="col">Date</th>
+                        <th scope="col">Type</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {% for event in event_type.list %}
+                        <tr>
+                            <td><a href="{% url 'event_details' event.id %}">{{event.name }}</a></td>
+                            <td>{{ event.date }}</td>
+                            <td>{{ event.get_category_display }}</td>
+                        </tr>
+                    {% endfor %}
+                </tbody>
+            </table>
+        </div>
+    {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/championship/templates/championship/update_organizer.html b/championship/templates/championship/update_organizer.html
index 3ae24811d5e178df466589c785edcd9d5205eac7..31205f26d5f2f61581b603da1b85f208058d6666 100644
--- a/championship/templates/championship/update_organizer.html
+++ b/championship/templates/championship/update_organizer.html
@@ -2,8 +2,14 @@
 
 {% load crispy_forms_tags %}
 
+{% block extrahead %}
+    {{ form.media }}
+{% endblock %}
+
 {% block content %}
     <h1>Edit organizer settings</h1>
+    <a class="btn btn-secondary" href="{% url 'address_list' %}">Edit Addresses</a>
+    <br>
     <form action="{% url 'organizer_update' %}" method="post">
         {% csrf_token %}
         {{ form|crispy }}
diff --git a/championship/tests/test_address.py b/championship/tests/test_address.py
new file mode 100644
index 0000000000000000000000000000000000000000..681ecdf1ec3b730f4269742f6c73205b3f228972
--- /dev/null
+++ b/championship/tests/test_address.py
@@ -0,0 +1,108 @@
+from django.test import TestCase, Client
+from django.urls import reverse
+from django.contrib.auth.models import User
+from championship.factories import AddressFactory, EventOrganizerFactory
+from championship.models import Address
+
+
+class BaseSetupTest(TestCase):
+    def base_set_up(self, with_address=True, username="testuser"):
+        self.client = Client()
+        self.user = User.objects.create_user(username=username, password="testpass")
+        self.organizer = EventOrganizerFactory(user=self.user, addresses=[])
+        if with_address:
+            self.address = AddressFactory(organizer=self.organizer)
+        self.client.login(username=username, password="testpass")
+
+
+class AddressListViewTest(BaseSetupTest):
+    def setUp(self):
+        self.base_set_up()
+
+    def test_view_url_exists(self):
+        response = self.client.get(reverse("address_list"))
+        self.assertEqual(response.status_code, 200)
+
+    def test_view_shows_address(self):
+        response = self.client.get(reverse("address_list"))
+        self.assertContains(response, str(self.address))
+
+
+class AddressCreateViewTest(BaseSetupTest):
+    def setUp(self):
+        self.base_set_up(with_address=False)
+
+    def test_view_url_exists(self):
+        response = self.client.get(reverse("address_create"))
+        self.assertEqual(response.status_code, 200)
+
+    def test_create_new_address(self):
+        self.assertEqual(Address.objects.count(), 0)
+        response = self.client.post(
+            reverse("address_create"),
+            data={
+                "location_name": "Test Location",
+                "street_address": "Test Street",
+                "city": "Test City",
+                "postal_code": "123456",
+                "region": Address.Region.AARGAU,
+                "country": Address.Country.SWITZERLAND,
+                "set_as_organizer_address": True,
+            },
+        )
+        self.assertEqual(Address.objects.count(), 1)
+        self.assertEqual(response.url, reverse("address_list"))
+        self.organizer.refresh_from_db()
+        self.assertEqual(self.organizer.default_address.location_name, "Test Location")
+
+
+class AddressUpdateViewTest(BaseSetupTest):
+    def setUp(self):
+        self.base_set_up()
+
+    def test_view_url_exists(self):
+        response = self.client.get(reverse("address_edit", args=[self.address.pk]))
+        self.assertEqual(response.status_code, 200)
+
+    def test_update_address(self):
+        response = self.client.post(
+            reverse("address_edit", args=[self.address.pk]),
+            data={
+                "location_name": "New Location",
+                "street_address": "New Street",
+                "city": "New City",
+                "postal_code": "654321",
+                "region": Address.Region.BERN,
+                "country": Address.Country.SWITZERLAND,
+                "set_as_organizer_address": False,
+            },
+        )
+        self.assertEqual(response.url, reverse("address_list"))
+        self.address.refresh_from_db()
+        self.assertEqual(self.address.location_name, "New Location")
+
+
+class AddressDeleteViewTest(BaseSetupTest):
+    def setUp(self):
+        self.base_set_up()
+
+    def test_delete_default_address(self):
+        self.assertEqual(Address.objects.count(), 1)
+        response = self.client.post(reverse("address_delete", args=[self.address.pk]))
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(Address.objects.count(), 0)
+        self.organizer.refresh_from_db()
+        self.assertEqual(self.organizer.default_address, None)
+
+    def test_delete_not_owned_address(self):
+        self.client.logout()
+        self.base_set_up(with_address=False, username="testuser2")
+        response = self.client.post(reverse("address_delete", args=[self.address.pk]))
+        self.assertEqual(response.status_code, 403)
+        self.assertTrue(Address.objects.filter(pk=self.address.pk).exists())
+
+    def test_get_delete_view_not_allowed(self):
+        # To prevent csrf only post should be allowed
+        response = self.client.get(reverse("address_delete", args=[self.address.pk]))
+        self.assertEqual(response.status_code, 403)
+        self.assertTrue(Address.objects.filter(pk=self.address.pk).exists())
diff --git a/championship/tests/test_events_create.py b/championship/tests/test_events_create.py
index 1cf1bad6a4d16458f67076a684376c384b8d2369..c556f65a47a532065381471550edda273cf66934 100644
--- a/championship/tests/test_events_create.py
+++ b/championship/tests/test_events_create.py
@@ -3,7 +3,7 @@ from django.test import TestCase, Client
 from django.contrib.auth.models import User
 from django.urls import reverse
 from championship.models import Event, EventOrganizer
-from championship.factories import EventOrganizerFactory, EventFactory
+from championship.factories import AddressFactory, EventOrganizerFactory, EventFactory
 
 
 class EventCreationTestCase(TestCase):
@@ -182,6 +182,24 @@ class EventCreationTestCase(TestCase):
         resp = self.client.post(reverse("event_delete", args=[event.id]))
         self.assertEqual(404, resp.status_code)
 
+    def test_default_address_is_initial(self):
+        self.login()
+        to = EventOrganizerFactory(user=self.user)
+        respone = self.client.get(reverse("events_create"))
+        initial_address = respone.context["form"].initial["address"]
+        self.assertEquals(to.default_address.id, initial_address)
+
+    def test_initial_address_not_overwritten_by_default_address(self):
+        to = EventOrganizerFactory(user=self.user)
+        not_default_address = to.addresses.all()[1]
+        event = EventFactory(address=not_default_address, organizer=to)
+        self.login()
+        response = self.client.get(reverse("event_update", args=[event.id]))
+        self.assertEqual(200, response.status_code)
+        initial_address = response.context["form"].initial["address"]
+        self.assertNotEquals(not_default_address, to.default_address)
+        self.assertEquals(not_default_address.id, initial_address)
+
 
 class EventCopyTestCase(TestCase):
     def setUp(self):
@@ -221,3 +239,12 @@ class EventCopyTestCase(TestCase):
 
         resp = self.client.get(reverse("event_details", args=[event.id]))
         self.assertIn("Copy event", resp.content.decode())
+
+    def test_initial_address_not_overwritten_by_default_address(self):
+        self.login()
+        not_default_address = self.organizer.get_addresses()[1]
+        event = EventFactory(address=not_default_address, organizer=self.organizer)
+        respone = self.client.get(reverse("event_copy", args=[event.id]))
+        initial_address = respone.context["form"].initial["address"]
+        self.assertNotEquals(not_default_address, self.organizer.default_address)
+        self.assertEquals(not_default_address.id, initial_address)
diff --git a/championship/tests/test_organizer_details.py b/championship/tests/test_organizer_details.py
new file mode 100644
index 0000000000000000000000000000000000000000..7517fb8476a71016801a0145bde6b0e62cdd21cc
--- /dev/null
+++ b/championship/tests/test_organizer_details.py
@@ -0,0 +1,54 @@
+from django.test import TestCase, Client
+from django.urls import reverse
+from django.utils import timezone
+from championship.factories import EventOrganizerFactory, EventFactory
+from django.contrib.auth import get_user_model
+
+User = get_user_model()
+
+
+class EventOrganizerDetailViewTests(TestCase):
+    def setUp(self):
+        self.client = Client()
+        self.user = User.objects.create_user(username="testuser", password="12345")
+        self.client.login(username="testuser", password="12345")
+
+        self.organizer = EventOrganizerFactory(user=self.user)
+
+        tomorrow = timezone.now() + timezone.timedelta(days=1)
+        past_date = timezone.now() - timezone.timedelta(days=5)
+
+        self.future_event = EventFactory(organizer=self.organizer, date=tomorrow)
+        self.past_event = EventFactory(organizer=self.organizer, date=past_date)
+        self.response = self.client.get(
+            reverse("organizer_details", args=[self.organizer.id])
+        )
+
+    def test_organizer_detail_view(self):
+        self.assertEqual(self.response.status_code, 200)
+        self.assertTemplateUsed(self.response, "championship/organizer_details.html")
+        self.assertContains(self.response, self.organizer.name)
+        self.assertContains(self.response, self.future_event.name)
+        self.assertContains(self.response, self.past_event.name)
+
+    def test_organizer_detail_future_and_past(self):
+        self.assertTrue("all_events" in self.response.context)
+        self.assertEqual(len(self.response.context["all_events"]), 2)
+        # Test Future Events
+        self.assertEqual(
+            self.response.context["all_events"][0]["list"][0], self.future_event
+        )
+        # Test Past Events
+        self.assertEqual(
+            self.response.context["all_events"][1]["list"][0], self.past_event
+        )
+
+    def test_organizer_detail_view_no_organizer(self):
+        self.response = self.client.get(
+            reverse("organizer_details", args=[9999])
+        )  # assuming 9999 is an invalid ID
+        self.assertEqual(self.response.status_code, 404)
+
+    def test_organizer_reverse(self):
+        edit_organizer_url = reverse("organizer_update")
+        self.assertContains(self.response, f'href="{edit_organizer_url}"')
diff --git a/championship/tests/test_organizer_edit.py b/championship/tests/test_organizer_edit.py
index 0c1b4f757999f5122e271cbac4405014d3cbbac9..e8757a76e6cc82d0de9967c374ede28e36e3b58a 100644
--- a/championship/tests/test_organizer_edit.py
+++ b/championship/tests/test_organizer_edit.py
@@ -2,7 +2,7 @@ from django.test import TestCase, Client
 from django.contrib.auth.models import User
 from django.urls import reverse
 from championship.models import EventOrganizer
-from championship.factories import EventOrganizerFactory
+from championship.factories import AddressFactory, EventOrganizerFactory
 
 
 class EventCreationTestCase(TestCase):
@@ -40,7 +40,7 @@ class EventCreationTestCase(TestCase):
         to = EventOrganizerFactory(user=self.user)
         response = self.client.get("/")
         self.assertIn(
-            reverse("organizer_update"),
+            reverse("organizer_details", args=(to.id,)),
             response.content.decode(),
             "Logged in users should get a link to creating events",
         )
@@ -48,11 +48,17 @@ class EventCreationTestCase(TestCase):
     def test_post_data(self):
         self.login()
         to = EventOrganizerFactory(user=self.user)
+        new_address = to.addresses.all()[1]
+        self.assertNotEquals(to.default_address.id, new_address.id)
         data = {
             "contact": "foo@foo.org",
             "name": "My test events",
+            "default_address": new_address.id,
+            "description": "This is a test description",
         }
         self.client.post(reverse("organizer_update"), data=data)
         to = EventOrganizer.objects.get(user=self.user)
         self.assertEqual(to.name, data["name"])
         self.assertEqual(to.contact, data["contact"])
+        self.assertEqual(to.default_address.id, new_address.id)
+        self.assertEqual(to.description, data["description"])
diff --git a/championship/urls.py b/championship/urls.py
index ab3c613478ac2803288173759f18a4f24e50cbcc..b919b0fc4ec062c88f6fcb8794c4b493e95982fa 100644
--- a/championship/urls.py
+++ b/championship/urls.py
@@ -43,8 +43,21 @@ urlpatterns = [
         name="event_clear_results",
     ),
     path(
-        "organizer/edit", views.OrganizerProfileEdit.as_view(), name="organizer_update"
+        "organizer/<int:pk>",
+        views.EventOrganizerDetailView.as_view(),
+        name="organizer_details",
     ),
+    path(
+        "organizer/edit",
+        views.OrganizerProfileEditView.as_view(),
+        name="organizer_update",
+    ),
+    path("address/", views.AddressListView.as_view(), name="address_list"),
+    path("address/create/", views.AddressCreateView.as_view(), name="address_create"),
+    path(
+        "address/<int:pk>/edit/", views.AddressUpdateView.as_view(), name="address_edit"
+    ),
+    path("address/<int:pk>/delete/", views.address_delete, name="address_delete"),
     path("api/", include(api_router.urls)),
     path("api/formats/", views.ListFormats.as_view(), name="formats-list"),
 ]
diff --git a/championship/views.py b/championship/views.py
index 97162b842723f716b90e64204f704453605b22ce..2c10f6a320fbc6f22b6b68cc53563c93dd6eec5a 100644
--- a/championship/views.py
+++ b/championship/views.py
@@ -1,5 +1,4 @@
 import datetime
-import math
 import re
 import logging
 import os
@@ -10,7 +9,8 @@ from typing import *
 from django.core.exceptions import ImproperlyConfigured
 from django.shortcuts import render, get_object_or_404, redirect
 from django.views.generic.base import TemplateView
-from django.views.generic.edit import DeleteView, FormView, UpdateView
+from django.views.generic.list import ListView
+from django.views.generic.edit import DeleteView, FormView, UpdateView, CreateView
 from django.views.generic import DetailView
 from django.http import HttpResponseRedirect, HttpResponseForbidden
 from django.urls import reverse, reverse_lazy
@@ -262,6 +262,14 @@ class CreateEventView(LoginRequiredMixin, FormView):
     template_name = "championship/create_event.html"
     form_class = EventCreateForm
 
+    def get_form_kwargs(self):
+        kwargs = super(CreateEventView, self).get_form_kwargs()
+        kwargs["organizer"] = EventOrganizer.objects.get(user=self.request.user)
+        kwargs["initial"][
+            "address"
+        ] = self.request.user.eventorganizer.default_address.id
+        return kwargs
+
     def form_valid(self, form):
         event = form.save(commit=False)
         event.organizer = EventOrganizer.objects.get(user=self.request.user)
@@ -782,11 +790,92 @@ class FutureEventView(TemplateView):
     template_name = "championship/future_events.html"
 
 
-class OrganizerProfileEdit(LoginRequiredMixin, UpdateView):
+class EventOrganizerDetailView(DetailView):
     model = EventOrganizer
-    fields = ["name", "contact"]
+    template_name = "championship/organizer_details.html"
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        organizer = self.get_object()
+
+        future_events = Event.objects.filter(
+            organizer=organizer, date__gte=datetime.date.today()
+        ).order_by("date")
+        past_events = Event.objects.filter(
+            organizer=organizer, date__lt=datetime.date.today()
+        ).order_by("-date")
+
+        all_events = []
+        if future_events:
+            all_events.append({"title": "Upcoming Events", "list": future_events})
+        if past_events:
+            all_events.append({"title": "Past Events", "list": past_events})
+        context["all_events"] = all_events
+        return context
+
+
+class OrganizerProfileEditView(LoginRequiredMixin, UpdateView):
     template_name = "championship/update_organizer.html"
-    success_url = reverse_lazy("organizer_update")
+    form_class = OrganizerProfileEditForm
 
     def get_object(self):
-        return EventOrganizer.objects.get(user=self.request.user)
+        return get_object_or_404(EventOrganizer, user=self.request.user)
+
+    def get_success_url(self):
+        return self.get_object().get_absolute_url()
+
+    def form_valid(self, form):
+        messages.success(self.request, "Succesfully updated organizer profile!")
+        return super().form_valid(form)
+
+
+class AddressListView(LoginRequiredMixin, ListView):
+    model = Address
+    template_name = "championship/address_list.html"
+
+    def get_queryset(self):
+        return self.request.user.eventorganizer.get_addresses()
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context["organizer_url"] = self.request.user.eventorganizer.get_absolute_url()
+        return context
+
+
+class AddressViewMixin:
+    model = Address
+    form_class = AddressForm
+    template_name = "championship/address_form.html"
+    success_url = reverse_lazy("address_list")
+
+    def form_valid(self, form):
+        organizer = self.request.user.eventorganizer
+        form.instance.organizer = organizer
+        self.object = form.save()
+        if form.cleaned_data["set_as_organizer_address"]:
+            organizer.default_address = self.object
+            organizer.save()
+        return super().form_valid(form)
+
+
+class AddressCreateView(LoginRequiredMixin, AddressViewMixin, CreateView):
+    pass
+
+
+class AddressUpdateView(LoginRequiredMixin, AddressViewMixin, UpdateView):
+    def get_queryset(self):
+        return self.request.user.eventorganizer.get_addresses()
+
+
+@login_required
+def address_delete(request, pk):
+    address = get_object_or_404(Address, id=pk)
+    if (
+        request.method == "POST"
+        and address in request.user.eventorganizer.get_addresses()
+    ):
+        address.delete()
+        messages.success(request, "Succesfully deleted address!")
+        return HttpResponseRedirect(reverse("address_list"))
+    else:
+        return HttpResponseForbidden("You are not authorized to delete this address!")